TL; DR /エグゼクティブサマリー:質問のこの部分について:
がに設定されているときCATCH
にコミットできるトランザクションを使用して、どのような場合にコントロールを内部に渡すことができるかわかりません。XACT_ABORT
ON
私は今、この上でテストするのかなりを行っていると私はいかなる場合も見つけることができませんXACT_STATE()
戻って1
内部のCATCH
ブロック@@TRANCOUNT > 0
とのセッションプロパティがXACT_ABORT
あるしON
。実際、SET XACT_ABORTの現在のMSDNページによると:
SET XACT_ABORTがONの場合、Transact-SQLステートメントでランタイムエラーが発生すると、トランザクション全体が終了してロールバックされます。
その声明は、あなたの憶測と私の調査結果と一致しているようです。
についてのMSDN記事にSET XACT_ABORT
は、トランザクション内の一部のステートメントが正常に実行され、一部のステートメントXACT_ABORT
がOFF
本当ですが、その例のステートメントはブロック内にありません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
ドキュメントの「コミットできない」トランザクション状態のすべて)。
次のように結論付けるのが妥当だと思います。
TRY...CATCH
SQL Server 2005 での構造の導入により、新しいトランザクション状態(つまり「コミット不能」)とXACT_STATE()
その情報を取得する関数が必要になりました。
- ブロックのチェック
XACT_STATE()
インCATCH
は、次の両方が当てはまる場合にのみ意味があります。
XACT_ABORT
であるOFF
(それ以外XACT_STATE()
は常に返す必要があります-1
し、@@TRANCOUNT
あなたが必要なすべてのだろう)
CATCH
ブロック内、または呼び出しがネストされている場合はチェーンのどこかにロジックがあり、を実行する代わりに変更(COMMIT
またはDML、DDLなどのステートメント)を行いますROLLBACK
。(これは非常に特殊な使用例です)**更新プログラム3セクションの下部にある、MicrosoftによるのXACT_STATE()
代わりに常にチェックするという非公式の推奨事項についての注意@@TRANCOUNT
と、テストで推論がうまくいかないことが示されている理由をご覧ください。
- 導入
TRY...CATCH
SQL Server 2005で構築体は、ほとんどの部分は、廃止しているXACT_ABORT ON
ことは、トランザクションを制御の大きな度合い(あなたは、少なくともするオプションを持っているために提供し、セッションのプロパティをCOMMIT
その提供、XACT_STATE()
戻りません-1
)。
これを見てのもう一つの方法は、されたSQL Server 2005の前に、XACT_ABORT ON
チェックと比較して、エラーが発生したときに処理を停止するための簡単かつ信頼性の高い方法を提供し@@ERROR
、それぞれの文の後に。
- ドキュメントのサンプルコードは
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
ブロック内でチェックすることは無意味です。-1
XACT_ABORT
ON
それでは、何の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
設定されていないという問題、あるいはその両方かもしれません。また、リンクサーバーや分散トランザクションを使用する場合、および/またはを使用する必要があると言ったものを読んだことを思い出しますが、今はそのドキュメントを見つけることができません。見つかったら、リンクで更新します。ON
XACT_STATE()
XACT_ABORT ON
XACT_STATE()
それでも、私はいくつかのことを試してみXACT_ABORT ON
ましたがCATCH
、XACT_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_ABORT
がOFF
レガシーの理由でデフォルトであり、通常はに設定する必要があると言いON
ます。
...
「...推奨事項に従ってSET XACT_ABORT ONで実行すると、トランザクションは常に終了します。」
XACT_ABORT ON
どこでも使用する前に、私は疑問に思う:ここで正確に何が得られているのか?私はそれをする必要があるとは思っていませんし、必要なときだけ使うべきだと一般的に主張しています。ROLLBACK
@Remusの回答に示されているテンプレートを使用することで十分に簡単に処理できるかどうか、またはこの回答に示されているように、基本的に同じことですが保存ポイントなしで何年も使用しているテンプレートネストされた呼び出しを処理します):
C#コードおよびストアドプロシージャでトランザクションを処理する必要がありますか
更新2
私はもう少しテストを行いました。今度は小さな.NETコンソールアプリを作成し、SqlCommand
オブジェクトを実行する前に(つまりを介してusing (SqlTransaction _Tran = _Connection.BeginTransaction()) { ...
)アプリ層でトランザクションを作成し、ステートメントだけでなくバッチ中止エラーを使用しました-エラーを中止し、次のことがわかりました:
- 「コミットできない」トランザクションとは、ほとんどの部分がすでにロールバックされている(変更は取り消されている)が、
@@TRANCOUNT
まだ0 より大きいトランザクションです。
- 「コミットできない」トランザクションがある場合、トランザクションを「コミットできない
COMMIT
」ことを示すエラーを生成して発行することはできません。また、それを無視することもできません/何もしないでください。バッチが長引くコミット不能なトランザクションで完了したことを示すバッチが終了するとエラーが生成され、ロールバックされます(とにかく、自動ロールバックする場合はなぜエラーを投げるのですか?)。したがって、おそらく即時ブロックではなく、バッチが終了する前に、明示的なを発行する必要があります。ROLLBACK
CATCH
- では
TRY...CATCH
際構築物XACT_ABORT
であるOFF
、トランザクションを終了させるエラーは自動的に、彼らは外に発生したTRY
「uncommitable」としてそれを残して、Tranasctionを終了する作業を元に戻すことはなくなり、そのようなバッチ中止エラーとして、ブロック。を発行することROLLBACK
は、トランザクションをクローズするために必要な形式的なものですが、作業はすでにロールバックされています。
- 場合
XACT_ABORT
でON
、ほとんどのエラーは、バッチ中止として作用し、真上(#3)の箇条書きに記載されているように、したがって振る舞います。
XACT_STATE()
、少なくともCATCH
ブロック内-1
で、エラー発生時にアクティブなトランザクションがあった場合、バッチ中止エラーを表示します。
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()
リターン1
でCATCH
ブロックをするとき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()
ません。1
SELECT
@@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
ました。
XACT_ABORT
にON
またはに設定すべきかどうかではありませんOFF
。