2008-09-16 17 views
77

私はちょうどTSQLの何かに驚いた。私は、XACT_ABORTをオンにした場合は、ストアドプロシージャ(または任意のバッチ)の実行を停止するxact_abortがオンのときにSQL Serverがraiserrorの後に実行を続けるのはなぜですか?

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

のようなものを呼び出すと思いました。

しかし、私のADO.NETエラーメッセージはちょうど反対を証明しました。私は、例外メッセージのraiserrorエラーメッセージとそれに続いて起こった次の問題の両方を得ました。

これは(とにかく私の習慣です)私の回避策ですが、それが必要でなければなりませんように、それはいないようです:

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

ドキュメントがこれを言う:

SET XACT_ABORTがある場合にはONの場合、Transact-SQL文で実行時エラーが発生すると、トランザクション全体が終了してロールバックされます。

明示的なトランザクションを使用している必要がありますか?

+0

重大度が16ではなく17または18に設定されている場合、 'RAISERROR'は実際に実行を終了します。 – reformed

答えて

43

あなたが同様の質問へのSQL Serverチームの応答によってConnect上で見ることができるように、これは、デザインTMことである:

はあなたのフィードバックをいただき、ありがとうございます。設計上、XACT_ABORTオプションはRAISERRORステートメントの動作に影響を与えません。今後のリリースのSQL Serverでこの動作を変更するようにフィードバックを検討します。

はい、これは(16のような)高い重症度とRAISERRORを望んだ一部の人のための問題のビットは、SQL実行エラーと同じになりますです - そうではありません。

回避策は、実行する必要があります。明示的なトランザクションを使用しても、変更する動作には何の影響もありません。

+0

ありがとうございました。あなたが参照したリンクは利用できないようです。 –

+2

リンクが正常に動作していれば、「RAISERRORはXACT_ABORTで動作します」、作者「jorundur」、ID:275308 – JohnC

22

try/catchブロックを使用すると、重大度11-19のraiserrorエラー番号によって、実行がcatchブロックにジャンプします。

16を超える重大度はシステムエラーです。次のコードをデモするには、try/catchブロックを設定し、失敗すると想定されるストアドプロシージャを実行します。 は、ストアドプロシージャがあると仮定します[我々はすぐにRAISERROR()

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

使用RETURNを実行し、それがさらに手順を実行していないだろうときDBO]。[AssumeThisFails]失敗しますました。

+8

「return」を呼び出す前に 'rollback transaction'を呼び出すことができます。 –

+1

おそらくキャッチブロックで何かをする必要があるかもしれません – sqluser

12

MSDNで指摘したように、RAISERRORの代わりにTHROWステートメントを使用する必要があります。

slightly differentlyの2つが動作します。しかし、XACT_ABORTがONに設定されている場合は、常にTHROWコマンドを使用する必要があります。

+24

2k12(またはそれが出てくるときに)がなければ、THROWステートメントはありません。 –

関連する問題