Tham số đầu ra không được đặt nếu thủ tục lưu trữ không thành công trong TRY / CATCH


7

Trong SQL Server 2008 (nhưng cũng trong năm 2014). Hãy xem xét một thủ tục có một tham số đầu ra. Quy trình này có thể tạo ra lỗi (và sẽ trong ví dụ sau). Tôi lưu ý rằng hành vi của tham số đầu ra không giống nhau nếu chúng ta gọi thủ tục trong khối TRY/ CATCH.

Thí dụ:

create procedure test_output @result varchar(10) output
as
begin
    set @result = 'hello'
    raiserror('This is an error', 16,1)
    set @result = 'error'
end

Nếu chúng tôi khởi chạy thủ tục một cách đơn giản:

declare @res1 varchar(10)
exec test_output @result = @res1 out
print 'Result is: '+ isnull(@res1, 'empty')

chúng tôi nhận được (và tôi ổn với nó):

Msg 50000, Cấp 16, Trạng thái 1, Quy trình kiểm tra thủ tục, Dòng 7 [Batch Start Line 12]
Đây là một lỗi
Kết quả là: lỗi

Nếu quy trình hiện đang ở trong khối thử / bắt:

declare @res2 varchar(10)
declare @error_message varchar(max)
begin try
    exec test_output @result = @res2 out
end try
begin catch
    set @error_message = error_message()
    raiserror(@error_message, 16,1)
    print 'Result is: '+ isnull(@res2, 'empty')
end catch

chúng tôi nhận được (và tôi buồn):

Msg 50000, Cấp 16, Bang 1, Dòng 28
Đây là lỗi
Kết quả là: trống

Thông báo lỗi là OK, nhưng tham số đầu ra bây giờ là NULL . Nếu, trong một TRY...CATCHbối cảnh, việc thực thi bị dừng lại ngay sau đó RAISERROR, tôi đã dự kiến ​​giá trị đầu ra sẽ được đặt thành hello .

Tại sao nó như vậy?

Câu trả lời:


5

Bạn đã có một khởi đầu tốt với thiết lập thử nghiệm đó, nhưng nó thiếu một cái gì đó khiến bạn hiểu sai những gì đang thực sự xảy ra. Nếu bạn đặt các PRINTcâu lệnh ở đầu, giữa và cuối của thủ tục được lưu trữ thì đầu ra bổ sung sẽ làm cho nó rõ ràng hơn về những gì đang diễn ra ở đây. Ví dụ:

USE [tempdb];
GO
CREATE PROCEDURE test_output
(
  @Result VARCHAR(10) OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;

    SET @Result = 'hello';

    PRINT 1;
    RAISERROR('This is an error', 16, 1);

    PRINT 2;
    SET @Result = 'error';

    PRINT 3;
END;
GO

Đầu ra từ truy vấn thử nghiệm đầu tiên của bạn là:

1
Msg 50000, Level 16, State 1, Procedure test_output, Line xxxx [Batch Start Line yyyyy]
This is an error
2
3
Result is: error

Và đó có lẽ là những gì bạn đang mong đợi nào. Nhưng, đầu ra từ truy vấn thử nghiệm thứ hai là:

1
Msg 50000, Level 16, State 1, Line xxxxx
This is an error
Result is: empty

Đó là một chút khác biệt. Bây giờ chúng ta có thể thấy rằng trong TRY...CATCHcấu trúc, việc thực thi bị dừng ngay lập tức khi RAISERRORđược gọi (nghĩa là nó trở thành một sự kiện hủy bỏ hàng loạt). Mặt khác, RAISERRORkhông ngay lập tức dừng thực thi khi không được gọi trong một TRY...CATCHcấu trúc.

Tuy nhiên, như bạn đã chỉ ra trong bản cập nhật của mình cho câu hỏi, điều này không giải thích tại sao OUTPUTtham số không được đặt thành hello. Đó là do ý định của hành vi thủ tục được lưu trữ bình thường để không phản ánh thực thi một phần (do lỗi hủy bỏ hàng loạt). Điều này được thảo luận trong bài viết sau đây:

TSQL Basics II - Thông số ngữ nghĩa vượt qua

Ý nghĩa: mặc dù thủ tục được lưu trữ đã thực hiện bước đặt biến thành hello, nhưng RAISERRORbây giờ là lỗi hủy bỏ hàng loạt khi được gọi trong một TRY...CATCHcấu trúc và các thủ tục được lưu trữ không phản ánh bất kỳ thay đổi nào đối với tham số khi chúng bị hủy bỏ.

Hành vi này cũng là trung tâm của giải thích sau đây:

Tại sao TVP phải S READN SÀNG và tại sao các tham số của các loại khác không thể S READN SÀNG

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.