为什么在xact_abort打开时,Sql Server在raiserror之后继续执行?

我只是感到惊讶的东西在TSQL。 我认为,如果xact_abort打开,调用类似

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

会停止执行存储过程(或任何批处理)。

但是我的ADO.NET错误信息刚好相反。 在exception消息中,我得到了两个raiserror错误消息,再加上之后破坏的下一个消息。

这是我的解决方法(无论如何,这是我的习惯),但它似乎不应该是必要的:

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

文档说这个:

当SET XACT_ABORT为ON时,如果Transact-SQL语句引发运行时错误,则整个事务将终止并回滚。

这是否意味着我必须使用明确的交易?

这是By Design TM ,正如您在Connect上看到的,SQL Server团队对类似问题的回应:

感谢您的反馈意见。 通过devise,XACT_ABORT设置选项不会影响RAISERROR语句的行为。 我们将考虑您的反馈来修改SQL Server未来版本的此行为。

是的,这对于一些希望RAISERROR的严重程度(如16 )与SQL执行错误相同的问题有点问题 – 事实并非如此。

你的解决方法就是你需要做什么,使用一个显式的事务对你想要改变的行为没有任何影响。

如果使用try / catch块,则严重性为11-19的raiserror错误编号将导致执行跳转到catch块。

任何严重性超过16是系统错误。 为了演示下面的代码设置一个try / catch块并执行一个我们认为会失败的存储过程:

假设我们有一个表[dbo]。[Errors]来保存错误假设我们有一个存储过程[dbo]。[AssumeThisFails]当我们执行时会失败

 -- 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 

RAISERROR()之后立即使用RETURN ,它不会进一步执行该过程。

正如在MSDN上指出的那样,应该使用THROW语句来代替RAISERROR

两人的performance略有不同 。 但是,当XACT_ABORT设置为ON时,您应该始终使用THROW命令。