XACT_ABORTがONに設定されている場合、どのような場合にCATCHブロック内からトランザクションをコミットできますか?


13

とについてMSDNを読んTRY...CATCHでいXACT_STATEます。

構造XACT_STATECATCHブロックで使用してTRY…CATCHトランザクションをコミットするかロールバックするかを決定する次の例があります。

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Test XACT_STATE for 0, 1, or -1.
    -- If 1, the transaction is committable.
    -- If -1, the transaction is uncommittable and should 
    --     be rolled back.
    -- XACT_STATE = 0 means there is no transaction and
    --     a commit or rollback operation would generate an error.

    -- Test whether the transaction is uncommittable.
    IF (XACT_STATE()) = -1
    BEGIN
        PRINT 'The transaction is in an uncommittable state.' +
              ' Rolling back transaction.'
        ROLLBACK TRANSACTION;
    END;

    -- Test whether the transaction is active and valid.
    IF (XACT_STATE()) = 1
    BEGIN
        PRINT 'The transaction is committable.' + 
              ' Committing transaction.'
        COMMIT TRANSACTION;   
    END;
END CATCH;
GO

私が理解していないのは、なぜ何XACT_STATEが返されるのかを気にしてチェックする必要があるのですか?

この例ではフラグXACT_ABORTが設定さONれていることに注意してください。

TRYブロック内に十分な重大エラーがある場合、コントロールはに渡されCATCHます。だから、私が内部にいる場合CATCH、トランザクションに問題があり、実際にこの場合に行うべき唯一の賢明なことはそれをロールバックすることですよね?

しかし、MSDNのこの例は、制御が渡される場合がありCATCH、それでもトランザクションをコミットするのが理にかなっていることを暗示しています。それが起こる可能性がある場合、それが理にかなっている場合、誰かが実際的な例を提供できますか?

がに設定されているときCATCHにコミットできるトランザクションを使用して、どのような場合にコントロールを内部に渡すことができるかわかりません。XACT_ABORTON

に関するMSDNの記事にSET XACT_ABORTは、トランザクション内の一部のステートメントが正常に実行XACT_ABORTされOFF、がに設定されているときに一部のステートメントが失敗する例があります。しかし、ブロック内で1 SET XACT_ABORT ONXACT_STATE()返すのはCATCHどうしてですか?

最初は、このコードを次のように書いていました。

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    PRINT 'Rolling back transaction.';
    ROLLBACK TRANSACTION;
END CATCH;
GO

Max Vernonによる回答を考慮して、このようなコードを記述します。彼は試みる前にアクティブなトランザクションがあるかどうかをチェックするのが理にかなっていることを示しましたROLLBACK。それでも、とのブロックすべてで運命のトランザクションまたはトランザクションなしのいずれかを持つことができます。したがって、いずれにしても、には何もありません。私が間違っている?SET XACT_ABORT ONCATCHCOMMIT

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    IF (XACT_STATE()) <> 0
    BEGIN
        -- There is still an active transaction that should be rolled back
        PRINT 'Rolling back transaction.';
        ROLLBACK TRANSACTION;
    END;

END CATCH;
GO

回答:


8

に設定されている場合、ブロック内からトランザクションをコミットできないことがわかります。CATCHXACT_ABORTON

このチェックXACT_STATEは、場合によっては1を返す可能性がありCOMMIT、トランザクションに対して可能性があることを示しているため、MSDNの例はやや誤解を招く可能性があります。

IF (XACT_STATE()) = 1
BEGIN
    PRINT 'The transaction is committable.' + 
          ' Committing transaction.'
    COMMIT TRANSACTION;   
END;

それは真実ではない、XACT_STATE内部の1を返すことはありませんCATCH場合はブロックXACT_ABORTに設定されていますON

MSDNサンプルコードはXACT_STATE()XACT_ABORT設定に関係なく関数の使用を主に説明することを目的としたようです。サンプルコードは、との両方のXACT_ABORTセットで動作するのに十分な汎用性がONありOFFます。ただXACT_ABORT = ONチェックでIF (XACT_STATE()) = 1不要になるということです。


Erland SommarskogによるSQL Serverのエラーおよびトランザクション処理に関する非常に詳細な一連の記事があります。でパート2 -エラーの分類と彼は一緒プットエラーのすべてのクラスとそれらがどのようにSQL Serverによって処理され、どのようにしているという包括的な表を提示TRY ... CATCHし、XACT_ABORT動作を変更します。

+-----------------------------+---------------------------++------------------------------+
|                             |     Without TRY-CATCH     ||        With TRY-CATCH        |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
|              SET XACT_ABORT |  OFF  |  ON   | OFF | ON  ||    ON or OFF     | OFF | ON  |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Class Name                  |    Aborts     |   Rolls   ||    Catchable     |   Dooms   |
|                             |               |   Back    ||                  |transaction|
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Fatal errors                |  Connection   |    Yes    ||       No         |    n/a    |
| Batch-aborting              |     Batch     |    Yes    ||       Yes        |    Yes    |
| Batch-only aborting         |     Batch     | No  | Yes ||       Yes        | No  | Yes |
| Statement-terminating       | Stmnt | Batch | No  | Yes ||       Yes        | No  | Yes |
| Terminates nothing at all   |    Nothing    |    No     ||       Yes        | No  | Yes |
| Compilation: syntax errors  |  (Statement)  |    No     ||       Yes        | No  | Yes |
| Compilation: binding errors | Scope | Batch | No  | Yes || Outer scope only | No  | Yes |
| Compilation: optimisation   |     Batch     |    Yes    || Outer scope only |    Yes    |
| Attention signal            |     Batch     | No  | Yes ||       No         |    n/a    |
| Informational/warning msgs  |    Nothing    |    No     ||       No         |    n/a    |
| Uncatchable errors          |    Varying    |  Varying  ||       No         |    n/a    |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+

表の最後の列が質問に答えます。TRY-CATCHしてXACT_ABORT ONトランザクションは、すべての可能なケースに運命にあります。

質問の範囲外の1つのメモ。アーランドが言うように、この一貫性は、設定XACT_ABORTする理由の1つですON

ストアドプロシージャにコマンドを含めることを推奨していますSET XACT_ABORT, NOCOUNT ON。上記の表を見るとXACT_ABORT、事実上、より高いレベルの一貫性があることがわかります。たとえば、トランザクションは常に運命づけられています。以下では、に設定XACT_ABORTした多くの例を示しOFFます。これにより、このデフォルト設定を回避する理由を理解できます。


7

私はこれに違ったアプローチをします。XACT_ABORT_ONはスレッジハンマーです。より洗練されたアプローチを使用できます。例外処理とネストされたトランザクションを参照してください。

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

このアプローチは、可能な場合、TRYブロック内で実行された作業のみをロールバックし、TRYブロックに入る前の状態に状態を復元します。このようにして、エラーの場合にすべての作業を失うことなく、カーソルの反復などの複雑な処理を実行できます。唯一の欠点は、トランザクションセーブポイントを使用することにより、分散トランザクションなど、セーブポイントと互換性のないものを使用できないようにすることです。


お返事ありがとうございますが、質問は実際XACT_ABORTONまたはに設定すべきかどうかではありませんOFF
ウラジミールバラノフ

7

TL; DR /エグゼクティブサマリー:質問のこの部分について:

がに設定されているときCATCHにコミットできるトランザクションを使用して、どのような場合にコントロールを内部に渡すことができるかわかりません。XACT_ABORTON

私は今、この上でテストするのかなりを行っていると私はいかなる場合も見つけることができませんXACT_STATE()戻って1内部のCATCHブロック@@TRANCOUNT > 0 のセッションプロパティがXACT_ABORTあるしON。実際、SET XACT_ABORTの現在のMSDNページによると

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

その声明は、あなたの憶測と私の調査結果と一致しているようです。

についてのMSDN記事にSET XACT_ABORTは、トランザクション内の一部のステートメントが正常に実行され、一部のステートメントXACT_ABORTOFF

本当です、その例のステートメントはブロック内にありませんTRY。内のものと同じステートメントTRYブロックは、まだエラーの原因となった1後の任意のステートメントの実行を妨げるが、それは仮定XACT_ABORTであるOFFコントロールが渡されたときに、CATCHトランザクションは、まだ以前の変更のすべてがエラーなしで起こったことで、物理的に有効であるブロックそして、することができますそれは願望であれば、コミットすること、またはそれらをロールバックすることができます。一方、場合XACT_ABORTされON、その後、事前の変更が自動的にロールバックされ、その後、あなたはどちらかに選択肢を与えられている。)発行AROLLBACKこれは、トランザクションがすでにロールバックされてからにリセットさ@@TRANCOUNTれてい0ないか、b)エラーが発生しているため、ほとんどの状況を受け入れるだけです。選択の余地はありませんか?

以下のためにそのドキュメントに明らかではないこのパズルの一つの可能性の重要な詳細は、SET XACT_ABORTこのセッションのプロパティ、およびそのサンプルコードでも、以来の周りされていることであるSQL Server 2000のより以前の、(ドキュメントがバージョン間でほぼ同じである)TRY...CATCHした構造をSQL Server 2005で導入されました。そのドキュメントを再度確認し、例を(なしTRY...CATCH)見ると、を使用するXACT_ABORT ONと、トランザクションがすぐにロールバックされます。トランザクション状態が「コミット不能」ではありませんそのSET XACT_ABORTドキュメントの「コミットできない」トランザクション状態のすべて)。

次のように結論付けるのが妥当だと思います。

  1. TRY...CATCHSQL Server 2005 での構造の導入により、新しいトランザクション状態(つまり「コミット不能」)とXACT_STATE()その情報を取得する関数が必要になりました。
  2. ブロックのチェックXACT_STATE()インCATCHは、次の両方が当てはまる場合にのみ意味があります。
    1. XACT_ABORTであるOFF(それ以外XACT_STATE()は常に返す必要があります-1し、@@TRANCOUNTあなたが必要なすべてのだろう)
    2. CATCHブロック内、または呼び出しがネストされている場合はチェーンのどこかにロジックがあり、実行する代わりに変更(COMMITまたはDML、DDLなどのステートメント)行いますROLLBACK。(これは非常に特殊な使用例です)**更新プログラム3セクションの下部にある、MicrosoftによるのXACT_STATE()代わりに常にチェックするという非公式の推奨事項についての注意@@TRANCOUNTと、テストで推論がうまくいかないことが示されている理由をご覧ください。
  3. 導入TRY...CATCHSQL Server 2005で構築体は、ほとんどの部分は、廃止しているXACT_ABORT ONことは、トランザクションを制御の大きな度合い(あなたは、少なくともするオプションを持っているために提供し、セッションのプロパティをCOMMITその提供、XACT_STATE()戻りません-1)。
    これを見てのもう一つの方法は、されたSQL Server 2005の前にXACT_ABORT ONチェックと比較して、エラーが発生したときに処理を停止するための簡単かつ信頼性の高い方法を提供し@@ERROR、それぞれの文の後に。
  4. ドキュメントのサンプルコードはXACT_STATE()誤っている、またはそれがためにチェック表示されていることで最高の状態で、誤解を招くXACT_STATE() = 1ときXACT_ABORTですON

長い部分;-)

はい、MSDNのサンプルコードは少しわかりにくいです(@@ TRANCOUNT(ロールバック)対XACT_STATEも参照);-)。そして、私はそれは(あなたがについて尋ねていることを理由:あなたも中に「コミット」トランザクションを持つことができるのは意味がありません、それ理由のいずれかを示して何か誤解さを感じるCATCHブロックXACT_ABORTであるがON)、あるいはそれが可能であるならば、それを技術的な可能性に焦点を当てており、少数の人がこれまでに必要とする、または必要とするものであり、必要になる可能性が高い理由を無視しています。

TRYブロック内に重大なエラーがある場合、コントロールはCATCHに渡されます。したがって、CATCH内にいる場合、トランザクションに問題があり、実際にこの場合に行うべき唯一の賢明なことは、それをロールバックすることです。

特定の単語や概念が何を意味するのかについて、同じページにいることを確認しておくと役立つと思います。

  • 「重大なエラー」:明確にするために、TRY ... CATCHはほとんどのエラーをトラップします。キャッチされないもののリストは、リンクされたMSDNページの「TRY…CATCH構造体に影響されないエラー」セクションにリストされています。

  • 「CATCH内にいる場合、トランザクションに問題があることを知っています」(em phasが追加されます):「トランザクション」とは、ステートメントを明示的なトランザクションにグループ化することによって決定される論理的な作業単位を意味する場合、おそらくはい。DBの人々の多くは、明示的なトランザクションをどのように、そしてなぜ使用し、どのステップがアトミックユニットを構成するべきかについて同様の見方をしているため、ロールバックは「行うべき唯一の賢明なこと」であることに同意する傾向があると思います仕事の。

    ただし、明示的なトランザクションにグループ化される実際の作業単位を意味する場合、いいえ、トランザクション自体に問題があることはわかりません。明示的に定義されたトランザクション内で実行されているステートメントがエラーを引き起こしたことを知っているだけです。ただし、DMLまたはDDLステートメントではない場合があります。また、たとえそれがDMLステートメントであったとしても、トランザクション自体はまだコミット可能です。

上記の2つのポイントを考えると、おそらく「コミットできない」トランザクションと「コミットしない」トランザクションを区別する必要があります。

XACT_STATE()返される場合1、それは、トランザクションが「コミット可能」であることを意味します。COMMITつまり、またはの間で選択できますROLLBACK。それをコミットしたくないかもしれませが、もし何らかの理由で偶然に来たくないなら、少なくともトランザクションのいくつかの部分が正常に完了したからです。

ただし、をXACT_STATE()返す場合、トランザクションの一部が不良状態になったため、-1本当に必要ROLLBACKです。制御がCATCHブロックに渡された@@TRANCOUNT場合、トランザクションをコミットできたとしても、なぜ確認したいのかを確認するだけで十分であることに同意します。

しかし、例の一番上に気づいた場合、設定XACT_ABORT ONによって状況が少し変わります。あなたはやった後、定期的にエラーを持つことができBEGIN TRANたときにはCATCHブロックに制御を渡しますXACT_ABORTですOFFとXACT_STATE()を返します1。ただし、XACT_ABORTがの場合ON、トランザクションは 'olエラーに対して "中止"(つまり無効化)されてからXACT_STATE()を返し-1ます。この場合、ブロックは常にwhen is を返すように見えるためXACT_STATE()CATCHブロック内でチェックすることは無意味です。-1XACT_ABORTON

それでは、何のXACT_STATE()ためですか?いくつかの手がかりは次のとおりです。

  • のMSDNページTRY...CATCH、「Uncommittable Transactions and XACT_STATE」セクションで、次のように述べています。

    通常、TRYブロック外のトランザクションを終了させるエラーにより、TRYブロック内でエラーが発生すると、トランザクションはコミット不能状態になります。

  • 「備考」セクションのSET XACT_ABORTのMSDNページには次のように記載されています。

    SET XACT_ABORTがOFFの場合、エラーを発生させたTransact-SQLステートメントのみがロールバックされ、トランザクションの処理が続行されます。

    そして:

    XACT_ABORTは、SQL Serverを含むほとんどのOLE DBプロバイダーに対する暗黙的または明示的なトランザクションのデータ変更ステートメントに対してONに設定する必要があります。

  • 「備考」セクションのBEGIN TRANSACTIONのMSDNページには次のように記載されています。

    ステートメントがコミットまたはロールバックされる前に次のアクションが実行されると、BEGIN TRANSACTIONステートメントによって開始されたローカルトランザクションは分散トランザクションにエスカレートされます。

    • リンクサーバー上のリモートテーブルを参照するINSERT、DELETE、またはUPDATEステートメントが実行されます。リンクサーバーへのアクセスに使用されるOLE DBプロバイダーがITransactionJoinインターフェイスをサポートしていない場合、INSERT、UPDATE、またはDELETEステートメントは失敗します。

最も適切な使用法は、リンクサーバーDMLステートメントのコンテキスト内にあるようです。そして、私は何年も前に自分でこれに遭遇したと信じています。私はすべての詳細を覚えていませんが、リモートサーバーが利用できないことと関係があり、何らかの理由で、そのエラーはTRYブロック内でキャッチされず、CATCHに送信されなかったため、あるべきではないときにCOMMIT もちろん、それチェックに失敗するのではなく、にXACT_ABORT設定されていないという問題、あるいはその両方かもしれません。また、リンクサーバーや分散トランザクションを使用する場合、および/またはを使用する必要があると言ったものを読んだことを思い出しますが、今はそのドキュメントを見つけることができません。見つかったら、リンクで更新します。ONXACT_STATE()XACT_ABORT ONXACT_STATE()

それでも、私はいくつかのことを試してみXACT_ABORT ONましたがCATCHXACT_STATE()レポートを使用してブロックに制御を渡し、ブロックに渡すシナリオを見つけることができません1

次の例を試しXACT_ABORTて、値の影響を確認してくださいXACT_STATE()

SET XACT_ABORT OFF;

BEGIN TRY
    BEGIN TRAN;

    SELECT 1/0 AS [DivideByZero]; -- error, yo!

    COMMIT TRAN;
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]

    IF (@@TRANCOUNT > 0)
    BEGIN
        ROLLBACK;
    END;
END CATCH;

GO ------------------------------------------------

SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRAN;

    SELECT 1/0 AS [DivideByZero]; -- error, yo!

    COMMIT TRAN;
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]

    IF (@@TRANCOUNT > 0)
    BEGIN
        ROLLBACK;
    END;
END CATCH;

GO ------------------------------------------------

SET XACT_ABORT ON;

BEGIN TRY
    SELECT 1/0 AS [DivideByZero]; -- error, yo!
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]
END CATCH;

更新

この回答のこれらのコメントに基づいて、元の質問の一部ではありませんが:

エラーとトランザクション処理に関するアーランドの記事を読んでいますが、彼はそれXACT_ABORTOFFレガシーの理由でデフォルトであり、通常はに設定する必要があると言いONます。
...
「...推奨事項に従ってSET XACT_ABORT ONで実行すると、トランザクションは常に終了します。」

XACT_ABORT ONどこでも使用する前に、私は疑問に思う:ここで正確に何が得られているのか?私はそれをする必要があるとは思っていませんし、必要なときだけ使うべきだと一般的に主張しています。ROLLBACK@Remusの回答に示されているテンプレートを使用することで十分に簡単に処理できるかどうか、またはこの回答に示されているように、基本的に同じことですが保存ポイントなしで何年も使用しているテンプレートネストされた呼び出しを処理します):

C#コードおよびストアドプロシージャでトランザクションを処理する必要がありますか


更新2

私はもう少しテストを行いました。今度は小さな.NETコンソールアプリを作成し、SqlCommandオブジェクトを実行する前に(つまりを介してusing (SqlTransaction _Tran = _Connection.BeginTransaction()) { ...)アプリ層でトランザクションを作成し、ステートメントだけでなくバッチ中止エラーを使用しました-エラーを中止し、次のことがわかりました:

  1. 「コミットできない」トランザクションとは、ほとんどの部分がすでにロールバックされている(変更は取り消されている)が、@@TRANCOUNTまだ0 より大きいトランザクションです。
  2. 「コミットできない」トランザクションがある場合、トランザクションを「コミットできないCOMMIT」ことを示すエラーを生成して発行することはできません。また、それを無視することもできません/何もしないでください。バッチが長引くコミット不能なトランザクションで完了したことを示すバッチが終了するとエラーが生成され、ロールバックされます(とにかく、自動ロールバックする場合はなぜエラーを投げるのですか?)。したがって、おそらく即時ブロックではなく、バッチが終了する前に、明示的なを発行する必要あります。ROLLBACKCATCH
  3. ではTRY...CATCH際構築物XACT_ABORTであるOFF、トランザクションを終了させるエラーは自動的に、彼らは外に発生したTRY「uncommitable」としてそれを残して、Tranasctionを終了する作業を元に戻すことはなくなり、そのようなバッチ中止エラーとして、ブロック。を発行することROLLBACKは、トランザクションをクローズするために必要な形式的なものですが、作業はすでにロールバックされています。
  4. 場合XACT_ABORTON、ほとんどのエラーは、バッチ中止として作用し、真上(#3)の箇条書きに記載されているように、したがって振る舞います。
  5. XACT_STATE()、少なくともCATCHブロック内-1で、エラー発生時にアクティブなトランザクションがあった場合、バッチ中止エラーを表示します。
  6. XACT_STATE()1アクティブなトランザクションがない場合でも返されることがあります。場合@@SPID(特に)がであるSELECTとともにリストXACT_STATE()、その後、XACT_STATE()アクティブなトランザクションがないとき1を返します。この動作はSQL Server 2012で開始され、2014年に存在しますが、2016年にはテストしていません。

上記の点を念頭に置いて:

  • 最も以来のポイント#4、#5を、(または全て?)のエラーが「uncommitable」トランザクションをレンダリングします考えると、チェックするために、完全に無意味なようでXACT_STATE()CATCHたときにブロックXACT_ABORTされON、戻り値は常になりますので、-1
  • チェックXACT_STATE()してCATCHたときにブロックXACT_ABORTされOFF、それが返されますので、戻り値は、少なくともいくつかのバリエーションがありますので、より理にかなって1文中止エラー。ただし、私たちのほとんどのようにコーディングする場合ROLLBACK、エラーが発生したという事実のためにとにかく呼び出すため、この区別は無意味です。
  • あなたは令状が発行しない状況見つけた場合COMMITにはCATCHブロックを、その後の値をチェックXACT_STATE()し、にしてくださいSET XACT_ABORT OFF;
  • XACT_ABORT ON構造に対してほとんどまたはまったく利益をもたらさないようTRY...CATCHです。
  • チェックXACT_STATE()するだけでチェックするよりも意味のある利点が得られるシナリオはありません@@TRANCOUNT
  • どこにも何のシナリオを見つけることができませんXACT_STATE()リターン1CATCHブロックをするときXACT_ABORTですON。これはドキュメントの間違いだと思います。
  • はい、明示的に開始しなかったトランザクションをロールバックできます。また、を使用するコンテキストでXACT_ABORT ONは、TRYブロックで発生したエラーが変更を自動的にロールバックするため、これは重要なポイントです。
  • TRY...CATCH構築物は、上の利益を有しXACT_ABORT ONていない自動的にトランザクション全体をキャンセルし、したがって(限り、トランザクションを可能にXACT_STATE()返す1(これはエッジの場合であっても)にコミットされます)。

XACT_STATE()を返す-1ときXACT_ABORTですOFF

SET XACT_ABORT OFF;

BEGIN TRY
    BEGIN TRAN;

    SELECT CONVERT(INT, 'g') AS [ConversionError];

    COMMIT TRAN;
END TRY
BEGIN CATCH
    DECLARE @State INT;
    SET @State = XACT_STATE();
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            @State AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage];

    IF (@@TRANCOUNT > 0)
    BEGIN
        SELECT 'Rollin back...' AS [Transaction];
        ROLLBACK;
    END;
END CATCH;

更新3

UPDATE 2セクションの項目#6に関連しています(XACT_STATE()アクティブなトランザクションがない場合に返される可能性のある誤った値):

  • SQL Server 2012で開始された奇妙な/誤った動作(これまでのところ、2012 SP2および2014 SP1に対してテスト済み)
  • SQL Serverバージョン2005、2008、および2008 R2ではXACT_STATE()、トリガーまたはINSERT...EXECシナリオで使用した場合に期待値が報告されませんでした:xact_state()を確実に使用してトランザクションが終了するかどうかを判断できません。ただし、これらの3つのバージョン(2008 R2でのみテストしました)では、withで使用したときに誤って報告されXACT_STATE()ませ1SELECT@@SPID
  • ここに記載されている動作に対して提出された接続バグがありますが、「設計」として閉じられています:XACT_STATE()は、SQL 2012で誤ったトランザクション状態を返す可能性があります。ただし、テストはDMVから選択するときに行われ、少なくとも一部のDMVについては、そうすることでシステム生成トランザクションが自然に発生すると結論付けられました。また、MSの最終回答には次のように記載されていました。

    IFステートメント、およびFROMなしのSELECTもトランザクションを開始しないことに注意してください。
    たとえば、以前に既存のトランザクションがない場合にSELECT XACT_STATE()を実行すると、0が返されます。

    次の例を考えると、これらのステートメントは正しくありません。

    SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @@SPID AS [SPID];
    GO
    DECLARE @SPID INT;
    SET @SPID = @@SPID;
    SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @SPID AS [SPID];
    GO
  • そのため、新しい接続のバグ:
    XACT_STATE()は、一部のシステム変数を使用し、FROM句を使用しないSELECTで使用すると1を返します

「XACT_STATE()がSQL 2012で誤ったトランザクション状態を返す可能性がある」ことに注意してください。

@@ trancountは、BEGIN TRANステートメントの数を返します。したがって、アクティブなトランザクションがあるかどうかの信頼できるインジケータではありません。XACT_STATE()は、アクティブな自動コミットトランザクションがある場合にも1を返すため、アクティブなトランザクションがあるかどうかのより信頼性の高いインジケータです。

ただし、信頼しない理由はありません@@TRANCOUNT。次のテストは、自動コミットトランザクションで@@TRANCOUNT実際に戻ることを示しています1

--- begin setup
GO
CREATE PROCEDURE #TransactionInfo AS
SET NOCOUNT ON;
SELECT @@TRANCOUNT AS [TranCount],
       XACT_STATE() AS [XactState];
GO
--- end setup

DECLARE @Test TABLE (TranCount INT, XactState INT);

SELECT * FROM @Test; -- no rows

EXEC #TransactionInfo; -- 0 for both fields

INSERT INTO @Test (TranCount, XactState)
    EXEC #TransactionInfo;

SELECT * FROM @Test; -- 1 row; 1 for both fields

また、トリガーを使用して実際のテーブルでテストし、明示的なトランザクションが開始されていなくても@@TRANCOUNT、トリガー内で正確に報告し1ました。


4

防御的プログラミングでは、可能な限り多くの既知の状態を処理するコードを記述する必要があり、それによってバグの可能性を減らします。

XACT_STATE()をチェックして、ロールバックを実行できるかどうかを判断するのは、単に良い方法です。ロールバックを盲目的に試みると、TRY ... CATCH内で誤ってエラーが発生する可能性があります。

TRY ... CATCH内でロールバックが失敗する1つの方法は、トランザクションを明示的に開始しなかった場合です。コードブロックをコピーして貼り付けると、簡単にこれが発生する可能性があります。


お返事ありがとうございます。シンプルROLLBACKが内部で機能しないケースを考えることができなかったのでCATCH、良い例を挙げました。ネストされたトランザクションと、独自のネストされたストアドプロシージャTRY ... CATCH ... ROLLBACKが関係すると、すぐに混乱する可能性もあると思います。
ウラジミールバラノフ

それでも、2番目の部分に関する回答を拡張できれば幸いです- コミット可能なトランザクションでブロックIF (XACT_STATE()) = 1 COMMIT TRANSACTION; 内に入れるにはどうすればよいCATCHですか?内部から(可能性のある)いくつかのゴミをコミットする勇気はありませんCATCH。私の推論は、CATCH何かがうまくいかなかった場合、データベースの状態を信頼できないので、ROLLBACK何でも手に入れたいと思います。
ウラジミールバラノフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.