1つのストアドプロシージャから3つのストアドプロシージャが開始されたときにロールバックする方法


23

内部で3つのストアドプロシージャのみを実行するストアドプロシージャがあります。マスターSPが成功した場合、1つのパラメーターのみを使用して保存します。

マスターストアドプロシージャで最初のストアドプロシージャが正常に機能するが、2番目のストアドプロシージャが失敗した場合、マスターSPのすべてのSPが自動的にロールバックされますか、それともコマンドを実行する必要がありますか?

私の手順は次のとおりです。

CREATE PROCEDURE [dbo].[spSavesomename] 
    -- Add the parameters for the stored procedure here

    @successful bit = null output
AS
BEGIN
begin transaction createSavebillinginvoice
    begin Try
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

   BEGIN 

   EXEC [dbo].[spNewBilling1]

   END

   BEGIN 

   EXEC [dbo].[spNewBilling2]

   END

   BEGIN 

   EXEC [dbo].[spNewBilling3]

   END 

   set @successful  = 1

   end Try

    begin Catch
        rollback transaction createSavesomename
        insert into dbo.tblErrorMessage(spName, errorMessage, systemDate) 
             values ('spSavesomename', ERROR_MESSAGE(), getdate())

        return
    end Catch
commit transaction createSavesomename
return
END

GO

spNewBilling3エラーがスローされたが、ロールバックspNewBilling2またはをしたくない場合は、からspNewBilling1削除するだけです。[begin|rollback|commit] transaction createSavebillinginvoicespSavesomename
マイク14年

回答:


56

質問に示されているコードのみが与えられ、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
    • 明確にするために、COMMITwhen @@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 Bonce:「DML Query 4」を取り消します。@@TRANCOUNTまだ2です。
      • ROLLBACK TRAN B2回:「B」に対応する保存ポイントがないため、「DMLクエリ4」を取り消してからエラーを返します。@@TRANCOUNTまだ2です。
      • ROLLBACK TRAN Aonce:「DML Query 4」と「DML Query 3」を取り消します。@@TRANCOUNTまだ2です。
      • ROLLBACK TRAN A2回:「DMLクエリ4」、「DMLクエリ3」、「DMLクエリ2」を取り消します。@@TRANCOUNTまだ2です。
      • ROLLBACK TRAN A3回:「DMLクエリ4」、「DMLクエリ3」、「DMLクエリ2」を元に戻します。次に、トランザクション全体をロールバックします(残っていたのは「DML Query 1」のみでした)。@@TRANCOUNT現在は0です。
      • COMMIT 一度: @@TRANCOUNT 1になります。
      • COMMIT1回、ROLLBACK TRAN B1回: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...CATCHTRY...CATCHXACT_ABORT ONOPENQUERYSET XACT_ABORT ON;

  • トリガーの内部では、XACT_ABORT暗黙的にに設定されONます。これが原因となる任意のトリガー内のエラーがトリガーを起動した全体のDML文をキャンセルします。

  • 特にトランザクションを使用する場合は、常に適切なエラー処理が必要です。TRY...CATCHSQL Server 2005で導入された構築物は、ほぼすべての状況、テストのためにオーバー歓迎の改善を処理する手段を提供して@@ERRORバッチ中止エラーであまり助けにはならなかった各文の後。

    TRY...CATCHただし、新しい「状態」を導入しました。ときではない使用してTRY...CATCH構文を使用すると、アクティブなトランザクションを持っているとエラーが発生した場合、その後、撮影することができますいくつかのパスがあります。

    • XACT_ABORT OFFおよびステートメント中止エラー:トランザクションはまだアクティブであり、次のステートメントがあれば処理が続行されます。
    • XACT_ABORT OFFほとんどのバッチ中止エラー:トランザクションはまだアクティブであり、次のバッチがある場合は処理が続行されます。
    • XACT_ABORT OFF特定のバッチ中止エラー:トランザクションはロールバックされ、次のバッチがある場合は処理が続行されます。
    • XACT_ABORT ONそして、任意のエラー:トランザクションがされ、ロールバック処理は、次を続行バッチがあれば、。


    ただし、を使用する場合TRY...CATCH、バッチ中止エラーはバッチを中止せず、代わりにCATCHブロックに制御を移します。ときXACT_ABORTOFF、トランザクションは、まだ時間の大半アクティブになり、あなたはにする必要がありますCOMMIT、または最も可能性が高いですROLLBACK。(などのように特定のバッチ中止エラーに遭遇したときでもOPENQUERY)する場合、またはXACT_ABORTあるON、トランザクションは、新しい状態で「uncommitable」になります。この状態でCOMMITは、DML操作を行うこともできません。できることはROLLBACKSELECTステートメントだけです。ただし、この「コミット不能」状態では、トランザクションはエラーが発生するとロールバックされ、発行ROLLBACKは単なる形式ですが、実行する必要があります。

    関数XACT_STATEを使用して、トランザクションがアクティブであるか、コミットできないか、存在しないかを判断できます。ifをテストするのではなく、CATCHブロック内のこの関数をチェックして、結果が-1(つまりコミットできない)かどうかを判断することを(少なくとも、一部の人は)お勧めし@@TRANCOUNT > 0ます。しかし、XACT_ABORT ONでは、それが可能な唯一の状態である必要があるため、とのテストは同等である@@TRANCOUNT > 0と思われますXACT_STATE() <> 0。とき一方、XACT_ABORTあるOFFアクティブなトランザクションがあり、どちらかの状態持たせることができる1-1CATCH発行することができるようになりますブロック、COMMIT代わりのをROLLBACK(私はときに誰かのためのケースを考えることはできません、が、したいだろうCOMMITトランザクションがコミット可能な場合)。DBA.SEの次の質問への回答に、ブロックXACT_STATE()内での使用に関する詳細情報と調査があります。XACT_ABORT がONに設定されている場合、CATCHブロック内からトランザクションをコミットできるのはどのような場合ですか?。特定のシナリオで誤って戻る小さなバグがあることに注意してください:XACT_STATE()は、FROM句なしでいくつかのシステム変数を使用してSELECTで使用すると1を返しますCATCHXACT_ABORT ONXACT_STATE()1


元のコードに関する注意:

  • トランザクションに役立たないため、トランザクションに指定された名前を削除できます。
  • あなたは各呼び出しを必要BEGINとしませんENDEXEC

2
それは本当に良い、良い、答えです。
マクネッツ

1
うわー、それは包括的な答えです!ありがとうございました!ところで、次のページでは、Try ... Catchによってトラップされないことを示唆するエラーに対処していますか?(「TRY…CATCH構造によって影響を受けないエラー」という見出しの下?technet.microsoft.com/en-us/library/ms175976
v

1
@jrdevdbaありがとう:-)。そしてようこそ。トラップされないエラーに関して、私はこれら2つをほぼ意味Compile errors, such as syntax errors, that prevent a batch from runningしましたErrors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of deferred name resolution.。しかし、それらはあまり頻繁には発生しません。そのような状況を見つけた場合は、それを修正するか(コードのバグの場合)、サブプロセスに配置して(EXECまたはsp_executesqlTRY...CATCHトラップできるようにします。
ソロモンラッツキー

2

はい、マスターストアドプロシージャのcatchステートメントのエラーロールバックコードが実行される場合、直接ステートメントまたはその中のネストされたストアドプロシージャによって実行されるすべての操作をロールバックします。

ネストされたストアドプロシージャで明示的なトランザクションを適用していない場合でも、これらのストアドプロシージャは暗黙的なトランザクションを使用し、完了時にコミットしますが、ネストされたストアドプロシージャで明示的または暗黙的なトランザクションを介してコミットしたSQL Serverエンジンはそれを無視しますマスターストアドプロシージャが失敗し、トランザクションがロールバックされる場合、これらのネストされたストアドプロシージャによるすべてのアクションをロールバックします。

トランザクションがコミットまたはロールバックされるたびに、最も外側のトランザクションの最後に実行されたアクションに基づいて。外側のトランザクションがコミットされると、内側のネストされたトランザクションもコミットされます。外部トランザクションがロールバックされると、内部トランザクションが個別にコミットされたかどうかに関係なく、すべての内部トランザクションもロールバックされます。

参照用http://technet.microsoft.com/en-us/library/ms189336(v=sql.105).aspx

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