92

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

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 ステートメントで実行時エラーが発生すると、トランザクション全体が終了し、ロールバックされます。

明示的なトランザクションを使用する必要があるということですか?

4

5 に答える 5

49

これは By Design TMです。Connectで、同様の質問に対する SQL Server チームの回答でわかるように:

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

RAISERRORはい、これは、深刻度の高いもの ( など16) が SQL 実行エラーと同じであることを期待していた人にとっては、ちょっとした問題です。そうではありません。

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

于 2008-09-16T20:12:29.123 に答える
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
于 2008-09-16T21:09:44.820 に答える
24

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

于 2011-05-13T11:16:40.017 に答える
15

のドキュメントで指摘されているようSET XACT_ABORTに、THROWステートメントは の代わりに使用する必要がありRAISERRORます。

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

于 2013-08-14T03:18:10.100 に答える
0

Microsoft は、raiserror の代わりに throw を使用することを提案しています。XACT_State を使用して、try catch ブロックのコミットまたはロールバックを決定します

set XACT_ABORT ON;

BEGIN TRY
     BEGIN TRAN;
    
     insert into customers values('Mark','Davis','markdavis@mail.com', '55909090');
    insert into customer values('Zack','Roberts','zackroberts@mail.com','555919191');
    COMMIT TRAN;
  END TRY

BEGIN CATCH
    IF XACT_STATE()=-1
        ROLLBACK TRAN;
    IF XACT_STATE()=1
       COMMIT TRAN;
    SELECT ERROR_MESSAGE() AS error_message
END CATCH
于 2021-08-03T14:07:44.793 に答える