質問に示されているコードのみが与えられ、3つのサブプロセスのいずれにも明示的なトランザクション処理がないと仮定すると、はい、3つのサブプロセスのいずれかのエラーがキャッチさROLLBACK
れ、CATCH
ブロック内のすべてがロールバックされます仕事の。
ただし、ここでトランザクションに関する注意事項がいくつかあります(少なくともSQL Serverで)。
何度呼び出しても、実際のトランザクションは1つだけです(最初のトランザクション)。BEGIN TRAN
- (ここで行ったように)トランザクションに名前を付けることができ、その名前はログに表示されますが、名前付けは最初/最も外側のトランザクションに対してのみ意味があります(再び、最初のトランザクションはトランザクションであるため)。
- を呼び出すたびに
BEGIN TRAN
、名前が付けられているかどうかにかかわらず、トランザクションカウンターが1ずつ増加します。
- を実行すると、現在のレベルを確認できます
SELECT @@TRANCOUNT;
- 次の
COMMIT
場合に発行されるコマンド@@TRANCOUNT
2以上でより多く、一度に一つ、トランザクションカウンタを減らすよりも何もしません。
COMMIT
が発行されるまで何もコミットされません@@TRANCOUNT
であります1
- 上記の情報が明確に示していない場合に備えて、トランザクションレベルに関係なく、トランザクションの実際のネストはありません。
セーブポイントを使用すると、トランザクション内で元に戻すことができる作業のサブセットを作成できます。
SAVE TRAN {save_point_name}
コマンドを介して保存ポイントが作成/マークされます
- セーブポイントは、トランザクション全体をロールバックせずに元に戻すことができる作業のサブセットの始まりを示します。
- セーブポイント名は一意である必要はありませんが、同じ名前を複数回使用すると、別個のセーブポイントが作成されます。
- セーブポイントはネストできます。
- セーブポイントはコミットできません。
- セーブポイントはで元に戻すことができます
ROLLBACK {save_point_name}
。(以下の詳細)
- セーブポイントをロールバックすると、ロールバックされたセーブポイントが作成された後に作成されたセーブポイントを含む、最後のを呼び出した後に発生したすべての作業が取り消さ
SAVE TRAN {save_point_name}
れます(したがって「ネスト」)。
- セーブポイントをロールバックしても、トランザクション数/レベルには影響しません
- トランザクション全体を
SAVE TRAN
発行することを除いて、最初の前に行われた作業を元に戻すことはできませんROLLBACK
。
- 明確にするために、
COMMIT
when @@TRANCOUNT
が2以上の場合、保存ポイントには影響しません(再び、1を超えるトランザクションレベルはそのカウンターの外側に存在しないため)。
特定の名前付きトランザクションをコミットすることはできません。トランザクション「名前」は、とともに提供された場合COMMIT
、無視され、読みやすくするためにのみ存在します。
ROLLBACK
名前なしで発行され、常にすべてのトランザクションをロールバックします。
ROLLBACK
名前で発行されたのいずれかに対応している必要があります。
- 名前が付けられていると
仮定した最初のトランザクション:SAVE TRAN
同じトランザクション名でno が呼び出されたと仮定すると、これはすべてのトランザクションをロールバックします。
- 「保存ポイント」(上記):
この動作は、最新の SAVE TRAN {save_point_name}
呼び出し以降に行われたすべての変更を「元に戻す」。
- 最初のトランザクションがa)という名前で、b)
SAVE TRAN
その名前でコマンドが発行された場合、そのトランザクション名の各ROLLBACKは、その名前がなくなるまで各セーブポイントを取り消します。その後、その名前で発行されたROLLBACKは、すべてのトランザクションをロールバックします。
たとえば、次のコマンドが示された順序で実行されたと仮定します。
BEGIN TRAN A -- @@TRANCOUNT is now 1
-- DML Query 1
SAVE TRAN A
-- DML Query 2
SAVE TRAN A
-- DML Query 3
BEGIN TRAN B -- @@TRANCOUNT is now 2
SAVE TRAN B
-- DML Query 4
ここで、発行する場合(次の各シナリオは互いに独立しています):
ROLLBACK TRAN B
once:「DML Query 4」を取り消します。@@TRANCOUNT
まだ2です。
ROLLBACK TRAN B
2回:「B」に対応する保存ポイントがないため、「DMLクエリ4」を取り消してからエラーを返します。@@TRANCOUNT
まだ2です。
ROLLBACK TRAN A
once:「DML Query 4」と「DML Query 3」を取り消します。@@TRANCOUNT
まだ2です。
ROLLBACK TRAN A
2回:「DMLクエリ4」、「DMLクエリ3」、「DMLクエリ2」を取り消します。@@TRANCOUNT
まだ2です。
ROLLBACK TRAN A
3回:「DMLクエリ4」、「DMLクエリ3」、「DMLクエリ2」を元に戻します。次に、トランザクション全体をロールバックします(残っていたのは「DML Query 1」のみでした)。@@TRANCOUNT
現在は0です。
COMMIT
一度: @@TRANCOUNT
1になります。
COMMIT
1回、ROLLBACK TRAN B
1回:1になり@@TRANCOUNT
ます。その後、「DMLクエリ4」を取り消します(COMMITが何もしなかったことを証明します)。@@TRANCOUNT
まだ1です。
トランザクション名と保存ポイント名:
- 32文字まで使用できます
- インスタンスレベルまたはデータベースレベルの照合に関係なく、バイナリ照合(ドキュメントが現在述べているように大文字と小文字を区別しない)を持つものとして扱われます。
- 詳細については、次の投稿の「トランザクション名」セクションを参照してください。「名前には何がありますか?:T-SQL識別子のWacky Worldの内部」
ストアドプロシージャ自体は、暗黙的なトランザクションではありません。明示的なトランザクションが開始されていない場合、各クエリは暗黙的なトランザクションです。これが、を実行するプログラム上の理由がない限り、単一のクエリに関する明示的なトランザクションが必要ない理由ROLLBACK
です。そうでない場合、クエリのエラーはそのクエリの自動ロールバックです。
ストアドプロシージャを呼び出すときは、呼び出さ@@TRANCOUNT
れたときと同じ値で終了する必要があります。つまり、次のことはできません。
BEGIN TRAN
呼び出し/親プロセスでコミットすることを期待して、コミットせずにprocで開始します。
- 0に
ROLLBACK
戻る@@TRANCOUNT
ため、procが呼び出される前に明示的なトランザクションが開始された場合、aを発行できません。
開始時よりも高いまたは低いトランザクションカウントでストアドプロシージャを終了すると、次のようなエラーが表示されます。
メッセージ266、レベル16、状態2、プロシージャYourProcName、行0
EXECUTE後のトランザクションカウントは、BEGINステートメントとCOMMITステートメントの数の不一致を示しています。前のカウント= X、現在のカウント=Y。
テーブル変数は、通常の変数と同様に、トランザクションにバインドされていません。
独立して呼び出すことができる(したがってトランザクション処理を必要とする)procでトランザクション処理を行うか、他のprocから呼び出す(トランザクション処理を必要としない)かについて:これはいくつかの異なる方法で実現できます。
うまく動作しているようです、私はここ数年のためにそれを処理してきたことの方法は、唯一にあるBEGIN
/ COMMIT
/ ROLLBACK
で、最も外側の層。サブプロシージャ呼び出しは、トランザクションコマンドをスキップします。以下に、各procに何を入れるかを説明します(トランザクション処理が必要な各proc)。
- 各プロシージャの上部で、
DECLARE @InNestedTransaction BIT;
simpleの代わりにBEGIN TRAN
:
IF (@@TRANCOUNT = 0)
BEGIN
SET @InNestedTransaction = 0;
BEGIN TRAN; -- only start a transaction if not already in one
END;
ELSE
BEGIN
SET @InNestedTransaction = 1;
END;
simpleの代わりにCOMMIT
:
IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
BEGIN
COMMIT;
END;
simpleの代わりにROLLBACK
:
IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
このメソッドは、トランザクションがSQL Server内で開始されたかどうか、またはアプリ層で開始されたかどうかに関係なく同じように機能する必要があります。
TRY...CATCH
コンストラクト内でのこのトランザクション処理の完全なテンプレートについては、次のDBA.SEの質問に対する回答を参照してください。C#コードおよびストアドプロシージャでトランザクションを処理する必要がありますか。
「基本」を超えて、注意すべきトランザクションのニュアンスがいくつかあります。
デフォルトでは、トランザクションは、ほとんどの場合、エラーが発生したときに自動的にロールバック/キャンセルされません。これは通常、適切なエラー処理があり、ROLLBACK
自分で電話をかけている限り、問題ではありません。ただし、バッチ中止エラーの場合や、使用中OPENQUERY
(または一般にリンクサーバー)でリモートシステムでエラーが発生した場合など、状況は複雑になることがあります。ほとんどのエラーはを使用してトラップできますがTRY...CATCH
、そのようにトラップできないエラーが2つあります(ただし、現時点ではどのエラーを覚えていないか、つまり研究中です)。これらの場合、SET XACT_ABORT ON
トランザクションを適切にロールバックするために使用する必要があります。
SET XACT_ABORT ONを使用すると、SQL Server はトランザクション(アクティブな場合)を直ちにロールバックし、エラーが発生した場合にバッチを中止します。この設定は、構造を導入したSQL Server 2005より前に存在していました。ほとんどの場合、ほとんどの状況を処理するため、の必要性はほとんどなくなります。ただし、使用するとき(および、現時点で思い出せない他のシナリオの1つ)、引き続き使用する必要があります。TRY...CATCH
TRY...CATCH
XACT_ABORT ON
OPENQUERY
SET XACT_ABORT ON;
トリガーの内部では、XACT_ABORT
暗黙的にに設定されON
ます。これが原因となる任意のトリガー内のエラーがトリガーを起動した全体のDML文をキャンセルします。
特にトランザクションを使用する場合は、常に適切なエラー処理が必要です。TRY...CATCH
SQL Server 2005で導入された構築物は、ほぼすべての状況、テストのためにオーバー歓迎の改善を処理する手段を提供して@@ERROR
バッチ中止エラーであまり助けにはならなかった各文の後。
TRY...CATCH
ただし、新しい「状態」を導入しました。ときではない使用してTRY...CATCH
構文を使用すると、アクティブなトランザクションを持っているとエラーが発生した場合、その後、撮影することができますいくつかのパスがあります。
XACT_ABORT OFF
およびステートメント中止エラー:トランザクションはまだアクティブであり、次のステートメントがあれば処理が続行されます。
XACT_ABORT OFF
ほとんどのバッチ中止エラー:トランザクションはまだアクティブであり、次のバッチがある場合は処理が続行されます。
XACT_ABORT OFF
特定のバッチ中止エラー:トランザクションはロールバックされ、次のバッチがある場合は処理が続行されます。
XACT_ABORT ON
そして、任意のエラー:トランザクションがされ、ロールバック処理は、次を続行バッチがあれば、。
ただし、を使用する場合TRY...CATCH
、バッチ中止エラーはバッチを中止せず、代わりにCATCH
ブロックに制御を移します。ときXACT_ABORT
でOFF
、トランザクションは、まだ時間の大半アクティブになり、あなたはにする必要がありますCOMMIT
、または最も可能性が高いですROLLBACK
。(などのように特定のバッチ中止エラーに遭遇したときでもOPENQUERY
)する場合、またはXACT_ABORT
あるON
、トランザクションは、新しい状態で「uncommitable」になります。この状態でCOMMIT
は、DML操作を行うこともできません。できることはROLLBACK
、SELECT
ステートメントだけです。ただし、この「コミット不能」状態では、トランザクションはエラーが発生するとロールバックされ、発行ROLLBACK
は単なる形式ですが、実行する必要があります。
関数XACT_STATEを使用して、トランザクションがアクティブであるか、コミットできないか、存在しないかを判断できます。ifをテストするのではなく、CATCH
ブロック内のこの関数をチェックして、結果が-1
(つまりコミットできない)かどうかを判断することを(少なくとも、一部の人は)お勧めし@@TRANCOUNT > 0
ます。しかし、XACT_ABORT ON
では、それが可能な唯一の状態である必要があるため、とのテストは同等である@@TRANCOUNT > 0
と思われますXACT_STATE() <> 0
。とき一方、XACT_ABORT
あるOFF
アクティブなトランザクションがあり、どちらかの状態持たせることができる1
か-1
でCATCH
発行することができるようになりますブロック、COMMIT
代わりのをROLLBACK
(私はときに誰かのためのケースを考えることはできません、が、したいだろうCOMMIT
トランザクションがコミット可能な場合)。DBA.SEの次の質問への回答に、ブロックXACT_STATE()
内での使用に関する詳細情報と調査があります。XACT_ABORT がONに設定されている場合、CATCHブロック内からトランザクションをコミットできるのはどのような場合ですか?。特定のシナリオで誤って戻る小さなバグがあることに注意してください:XACT_STATE()は、FROM句なしでいくつかのシステム変数を使用してSELECTで使用すると1を返しますCATCH
XACT_ABORT ON
XACT_STATE()
1
元のコードに関する注意:
- トランザクションに役立たないため、トランザクションに指定された名前を削除できます。
- あなたは各呼び出しを必要
BEGIN
としませんEND
EXEC
spNewBilling3
エラーがスローされたが、ロールバックspNewBilling2
またはをしたくない場合は、からspNewBilling1
削除するだけです。[begin|rollback|commit] transaction createSavebillinginvoice
spSavesomename