xact_abortがオンのときに、raiserrorの後でSQL Serverが実行し続けるのはなぜですか?


87

TSQLの何かに驚いたばかりです。xact_abortがオンの場合、次のように呼び出すと思いました

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

ストアドプロシージャ(または任意のバッチ)の実行を停止します。

しかし、私のADO.NETエラーメッセージは正反対でした。例外メッセージにraisrrorエラーメッセージと、その後に発生した次のメッセージの両方が表示されました。

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

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

ドキュメントはこれを言います:

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

それは私が明示的なトランザクションを使用している必要があることを意味しますか?


ただ、テストさとRAISERROR厳しさが17または18に設定されている場合、実際には16の代わりに、実行を終了します
改革

2
ただ、2012 SQL Serverのテストされ、RAISERROR実際にはなりません重大度が17または18に設定されている場合、16の代わりに、実行を終了
イアン・ボイド

1
その理由は、Erland Sommarskog(2001年以降のSQL Server MVP)の優れたシリーズ「SQLServerでのエラーとトランザクションの処理」のパート2で明確に説明されています。「SQLServerは意図的に次のように設計されているように感じます。可能な限り混乱させます。新しいリリースを計画するとき、ユーザーを混乱させるために今回は何ができるかをお互いに尋ねます。アイデアが少し不足することもありますが、誰かがエラー処理で何かをしようと言います。」
リバースエンジニア

@IanBoydは、実際、重大度を17、18、または19に設定した後でも、実行は停止されません。さらに興味深いのは、Messagesタブを見ると、メッセージが表示されない(X rows affected)か、PRINTメッセージが表示されないことです。これは完全なです。
ガブリエリウス

回答:


48

これはByDesign TMであり、同様の質問に対するSQLServerチームの回答によってConnectで確認できます。

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

はい、これはRAISERROR(のような16)重大度が高いとSQL実行エラーと同じになることを望んでいた人にとっては少し問題です-そうではありません。

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


1
フィリップに感謝します。参照したリンクは利用できないようです。
エリックZビアード

2
リンクは正常に機能しています。検索する必要がある場合は、タイトル「RAISERRORをXACT_ABORTで動作させます」、作成者「jorundur」、ID:275308
JohnC 2013

リンクが切れており、archive.orgのキャッシュコピーがありません。それは永遠に時の砂に失われました。
イアン・ボイド

この答えは良いバックアップです-この振る舞いが明らかにされているドキュメントへのリンクがあります。
pcdev

25

try / catchブロックを使用する場合、重大度11〜19のraisrrorエラー番号により、実行が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

22

RETURN直後に使用するRAISERROR()と、それ以上手順は実行されません。


8
を呼び出すrollback transaction前に呼び出すことをお勧めしますreturn
マイククリスチャン

1
おそらく、catchブロックで何かをする必要があるかもしれません
sqluser 2015年

14

以下のためのドキュメント上で指摘したようにSET XACT_ABORTTHROWステートメントが代わりに使用する必要がありますRAISERROR

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


25
2k12(またはそれ以上)がない場合は、THROWステートメントはありません。
ジェフモデン2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.