ストアドプロシージャで「SET XACT_ABORT ON」を使用する利点は何ですか?


回答:


231

SET XACT_ABORT ON実行時エラーが発生すると、トランザクション全体をロールバックし、バッチを中止するようにSQL Serverに指示します。SQL Server自体ではなく、クライアントアプリケーションで発生するコマンドタイムアウトなどの場合に対応します(既定のXACT_ABORT OFF設定ではカバーされません)。

クエリタイムアウトはトランザクションを開いたままにするので、SET XACT_ABORT ON開いているトランザクションとの接続でアプリケーションが作業を実行する結果は悲惨なので、明示的なトランザクションがあるすべてのストアドプロシージャで推奨されます(特別な理由がない限り)。

Dan Guzmanのブログには本当に素晴らしい概要があります。


41
では、なぜデフォルトでオンにならないのですか?
マイクW

1
BEGIN TRY-がBEGIN CATCHあり、SQL ROLLBACKBEGIN CATCHブロックがある場合、XACT_ABORTは引き続き必要ですか?
user20358

1
@ user20358 BEGIN TRY- BEGIN CATCHあなたは1を期待していない開いているトランザクションであなたを残して、あまりにも、クライアントアプリケーション上で発生するタイムアウトのようなものをキャッチし、いくつかのSQLエラーがキャッチできていることはありません。
トム・リント

37

私の意見では、SET XACT_ABORT ONは、SQL 2k5にBEGIN TRY / BEGIN CATCHを追加することで廃止されました。Transact-SQLの例外ブロックの前は、エラーを処理するのが本当に難しく、不均衡なプロシージャが一般的でした(出口と入口で異なる@@ TRANCOUNTがあったプロシージャ)。

Transact-SQLの追加により、トランザクションのバランスを適切に保つことが保証されている正しいプロシージャを書くのがはるかに簡単になります。たとえば、例外処理とネストされたトランザクションにこのテンプレートを使用します。

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end
go

回復可能なエラーが発生した場合に、自分の作業だけをロールバックするアトミックプロシージャを作成できます。

Transact-SQLプロシージャが直面する主な問題の1つはデータの純度です。受信したパラメーターまたはテーブル内のデータがまったく間違っている場合があり、その結果、重複キーエラー、参照制約エラー、チェック制約エラーなどが発生します。結局のところ、それがまさにこれらの制約の役割です。これらのデータ純度エラーが不可能であり、すべてがビジネスロジックによってキャッチされる場合、制約はすべて廃止されます(劇的な誇張が効果のために追加されます)。XACT_ABORTがONの場合、これらのすべてのエラーにより、例外を適切に処理する例外ブロックをコーディングするのではなく、トランザクション全体が失われます。典型的な例は、INSERTを実行しようとし、PK違反でUPDATEに戻ることです。


9
クライアントのタイムアウトを除いて...そして私の見方は、SET XACT_ABORTはSQL 2005でより効果的です。これは、動作がより予測可能であるため、バッチ中止エラーがはるかに少ないためです。
gbn 2009

7
多少は同意しますが、コマンドタイムアウトが発生した場合は開発者DBAが責任を負うことになるので、すべての不測の事態に対処するためにエラー処理を計画しています。
gbn 2009

4
@RemusRusanu長時間実行される同期データベース操作を他にどのように処理しますか?
Ian Boyd

5
MSDNドキュメントでは、「SQL Serverを含むほとんどのOLE DBプロバイダーに対して、暗黙的または明示的なトランザクションのデータ変更ステートメントに対してXACT_ABORTをONに設定する必要があります。このオプションが不要なのは、プロバイダーがネストされたトランザクションをサポートしている場合のみです。」msdn.microsoft.com/en-us/library/ms188792(v=sql.120).aspx
Nathan

4
「BEGIN TRY / BEGIN CATCHを追加したことでSET XACT_ABORT ONは廃止されたと思います」-聞きましたが、sommarskog.se / error_handling / Part1.html
エンジニア、

22

MSDNの引用:

SET XACT_ABORTがONの場合、Transact-SQLステートメントで実行時エラーが発生すると、トランザクション全体が終了し、ロールバックされます。SET XACT_ABORTがOFFの場合、エラーが発生したTransact-SQLステートメントのみがロールバックされ、トランザクションは処理を続行します。

実際には、これは一部のステートメントが失敗し、トランザクションが「部分的に完了」したままになる可能性があることを意味し、呼び出し側にはこの失敗の兆候がない場合があります。

簡単な例:

INSERT INTO t1 VALUES (1/0)    
INSERT INTO t2 VALUES (1/1)    
SELECT 'Everything is fine'

このコードは、XACT_ABORT OFFで「正常に」実行され、XACT_ABORT ONでエラーで終了します(「INSERT INTO t2」は実行されず、クライアントアプリケーションは例外を発生させます)。

より柔軟なアプローチとして、各ステートメントの後に@@ ERRORをチェックするか(古い学校)、またはTRY ... CATCHブロックを使用できます(MSSQL2005 +)。個人的には、高度なエラー処理の理由がない場合は常にXACT_ABORTをONに設定することを好みます。


8

クライアントのタイムアウトとそれらを処理するためのXACT_ABORTの使用に関して、私の意見では、SqlClientなどのクライアントAPIでタイムアウトを設定する理由は少なくとも1つあります。これは、SQLサーバーコードで発生するデッドロックからクライアントアプリケーションコードを保護するためです。この場合、クライアントコードに障害はありませんが、サーバー上でコマンドが完了するのを待って、クライアントコードが永久にブロックされるのを防ぐ必要があります。したがって、逆に、クライアントコードを保護するためにクライアントタイムアウトが存在する必要がある場合、クライアントが待機するよりもサーバーコードの実行に時間がかかる場合に備えて、XACT_ABORT ONはクライアントアボートからサーバーコードを保護する必要があります。


1

これはトランザクション管理で使用され、エラーが発生するとトランザクションがロールバックされるようにします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.