почему sql server продолжает работать после raiserror, когда xact\_abort включен?

<p>я просто удивился чему-то в tsql. я думал, что если xact\_abort был включен, вызывая что-то вроде</p><pre><code>raiserror('something bad happened', 16, 1); </code></pre><p>остановит выполнение хранимой процедуры (или любой пакет).</p><p>но мое сообщение об ошибке ado.net доказало обратное. я получил как сообщение об ошибке raiserror в сообщении об исключении, так и следующее, что сломалось после этого.</p><p>это мой обходной путь (который в любом случае является моей привычкой), но, похоже, это не нужно:</p><pre><code>if @somethingbadhappened begin; raiserror('something bad happened', 16, 1); return; end; </code></pre><p>документы говорят это:</p><blockquote> <p>когда set xact\_abort установлен в on, если инструкция transact-sql вызывает ошибку во время выполнения, вся транзакция завершается и откатывается.</p> </blockquote><p>означает ли это, что я должен использовать явную транзакцию?</p>

<p>это по дизайну<sup>tm</sup>, как вы можете видеть на <a href="http://connect.microsoft.com/sqlserver/feedback/viewfeedback.aspx?feedbackid=275308">connect</a> по ответу команды sql server на аналогичный вопрос:</p><blockquote> <p>спасибо за ваш отзыв. конструктивно параметр set xact\_abort не влияет на поведение оператора raiserror. мы рассмотрим ваши отзывы, чтобы изменить это поведение для будущего выпуска sql server.</p> </blockquote><p>да, это небольшая проблема для тех, кто надеялся, что <code>raiserror</code> с высокой степенью серьезности (например, <code>16</code>) будет таким же, как ошибка выполнения sql - это не так.</p><p>ваш обходной путь - это то, что вам нужно сделать, и использование явной транзакции не влияет на поведение, которое вы хотите изменить.</p>

<p>если вы используете блок try/catch, номер ошибки raiserror со степенью серьезности 11-19 вызовет переход к блоку catch.</p><p>любая серьезность выше 16 является системной ошибкой. для демонстрации следующего кода устанавливается блок try/catch и выполняется хранимая процедура, которая, как мы предполагаем, завершится неудачно:</p><p>предположим, у нас есть таблица [dbo]. [errors] для хранения ошибок. предположим, у нас есть хранимая процедура [dbo]. [assumethisfails], которая завершится ошибкой, когда мы ее выполним.</p><pre><code>-- 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 </code></pre>

<p>используйте <code>return</code> сразу после <code>raiserror()</code>, и процедура не будет выполняться дальше.</p>

<p>как указано в <a href="http://technet.microsoft.com/en-us/library/ms188792.aspx">msdn</a> оператор <code>throw</code> должен использоваться вместо <code>raiserror</code>.</p><p>они ведут себя <a href="http://technet.microsoft.com/en-us/library/ee677615.aspx">немного по-разному</a> . но когда для <code>xact\_abort</code> установлено значение on, вам всегда следует использовать команду <code>throw</code>.</p>