2008-09-16 48 views
77

Tôi vừa bị bất ngờ bởi điều gì đó trong TSQL. Tôi nghĩ rằng nếu xact_abort được bật, hãy gọi một số thứ nhưTại sao Sql Server tiếp tục thực hiện sau khi raiserror khi xact_abort được bật?

raiserror('Something bad happened', 16, 1); 

sẽ ngừng thực hiện quy trình được lưu trữ (hoặc bất kỳ lô nào).

Nhưng thông báo lỗi ADO.NET của tôi đã chứng minh điều ngược lại. Tôi đã nhận được cả thông báo lỗi raiserror trong thông báo ngoại lệ, cộng với điều tiếp theo đã phá vỡ sau đó.

Đây là cách giải quyết của tôi (đó là thói quen của tôi anyway), nhưng nó không có vẻ như nó phải là cần thiết:

if @somethingBadHappened 
    begin; 
     raiserror('Something bad happened', 16, 1); 
     return; 
    end; 

Các tài liệu nói điều này:

Khi SET XACT_ABORT là ON, nếu câu lệnh Transact-SQL làm tăng lỗi thời gian chạy, toàn bộ giao dịch sẽ bị chấm dứt và được khôi phục.

Điều đó có nghĩa là tôi phải sử dụng giao dịch rõ ràng?

+0

Chỉ cần thử nghiệm và 'RAISERROR' sẽ thực sự chấm dứt thực thi nếu mức độ nghiêm trọng được đặt thành 17 hoặc 18, thay vì 16. – reformed

Trả lời

43

này là do thiết kế TM, như bạn có thể nhìn thấy trên Connect bởi phản ứng Server đội của SQL cho một câu hỏi tương tự:

Cảm ơn bạn đã phản hồi của bạn. Theo thiết kế, tùy chọn thiết lập XACT_ABORT không ảnh hưởng đến hành vi của câu lệnh RAISERROR. Chúng tôi sẽ xem xét phản hồi của bạn để sửa đổi hành vi này cho bản phát hành SQL Server trong tương lai.

Vâng, đây là một chút của một vấn đề đối với một số người hy vọng RAISERROR với một mức độ nghiêm trọng cao (như 16) sẽ được giống như một lỗi thực thi SQL - nó không.

Giải pháp thay thế của bạn chỉ là về những gì bạn cần làm và việc sử dụng giao dịch rõ ràng không ảnh hưởng đến hành vi bạn muốn thay đổi.

+0

Cảm ơn Philip. Liên kết bạn đã tham chiếu dường như không khả dụng. –

+2

Liên kết đang hoạt động tốt, nếu bạn cần tìm kiếm, tiêu đề "Có RAISERROR hoạt động với XACT_ABORT", tác giả "jorundur", ID: 275308 – JohnC

22

Nếu bạn sử dụng khối try/catch, một lỗi lỗi số lỗi với mức độ nghiêm trọng 11-19 sẽ làm cho việc thực thi nhảy tới khối catch.

Mọi mức độ nghiêm trọng trên 16 là lỗi hệ thống. Để chứng minh đoạn mã sau thiết lập một khối try/catch và thực thi một thủ tục đã lưu trữ mà chúng tôi giả định sẽ thất bại:

giả sử chúng ta có một bảng [dbo]. [Lỗi] để giữ lỗi giả sử chúng ta có một thủ tục lưu sẵn [ dbo]. [AssumeThisFails] mà sẽ thất bại khi chúng ta thực hiện nó

-- first lets build a temporary table to hold errors 
if (object_id('tempdb..#RAISERRORS') is null) 
create table #RAISERRORS (ErrorNumber int, ErrorMessage varchar(400), ErrorSeverity int, ErrorState int, ErrorLine int, ErrorProcedure varchar(128)); 

-- this will determine if the transaction level of the query to programatically determine if we need to begin a new transaction or create a save point to rollback to 
declare @tc as int; 
set @tc = @@trancount; 
if (@tc = 0) 
begin transaction; 
else 
save transaction myTransaction; 

-- the code in the try block will be executed 
begin try 
declare @return_value = '0'; 
set @return_value = '0'; 
declare 
    @ErrorNumber as int, 
    @ErrorMessage as varchar(400), 
    @ErrorSeverity as int, 
    @ErrorState as int, 
    @ErrorLine as int, 
    @ErrorProcedure as varchar(128); 


-- assume that this procedure fails... 
exec @return_value = [dbo].[AssumeThisFails] 
if (@return_value <> 0) 
    raiserror('This is my error message', 17, 1); 

-- the error severity of 17 will be considered a system error execution of this query will skip the following statements and resume at the begin catch block 
if (@tc = 0) 
    commit transaction; 
return(0); 
end try 


-- the code in the catch block will be executed on raiserror("message", 17, 1) 
begin catch 
    select 
    @ErrorNumber = ERROR_NUMBER(), 
    @ErrorMessage = ERROR_MESSAGE(), 
    @ErrorSeverity = ERROR_SEVERITY(), 
    @ErrorState = ERROR_STATE(), 
    @ErrorLine = ERROR_LINE(), 
    @ErrorProcedure = ERROR_PROCEDURE(); 

    insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) 
    values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure); 

    -- if i started the transaction 
    if (@tc = 0) 
    begin 
    if (XACT_STATE() <> 0) 
    begin 
    select * from #RAISERRORS; 
    rollback transaction; 
    insert into [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) 
    select * from #RAISERRORS; 
    insert [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) 
    values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure); 
    return(1); 
    end 
    end 
    -- if i didn't start the transaction 
    if (XACT_STATE() = 1) 
    begin 
    rollback transaction myTransaction; 
    if (object_id('tempdb..#RAISERRORS') is not null) 
    insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) 
    values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure); 
    else 
    raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState); 
    return(2); 
    end 
    else if (XACT_STATE() = -1) 
    begin 
    rollback transaction; 
    if (object_id('tempdb..#RAISERRORS') is not null) 
    insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure) 
    values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure); 
    else 
    raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState); 
    return(3); 
    end 
end catch 
end 
19

Sử dụng RETURN ngay sau khi RAISERROR() và nó sẽ không thực hiện các thủ tục tiếp theo.

+8

Bạn có thể muốn gọi 'rollback transaction' trước khi gọi 'return'. –

+1

Có lẽ bạn có thể cần phải làm điều gì đó trong khối catch của bạn – sqluser

12

Như được chỉ ra trên MSDN tuyên bố THROW nên được sử dụng thay vì RAISERROR.

Hai ứng xử slightly differently. Nhưng khi XACT_ABORT được đặt thành BẬT, thì bạn nên luôn sử dụng lệnh THROW.

+24

Nếu bạn không có 2k12 (hoặc cao hơn khi nó xuất hiện), thì không có câu lệnh THROW nào. –

Các vấn đề liên quan