2012-03-15 15 views
12

ストアドプロシージャを使用してセーブポイントを設定する必要があるため、特定の状況ではすべての処理を取り消して呼び出し元にエラーコードを返すことができますそれを受け入れてコミットし、呼び出し元に成功を返します。しかし、私はそれが呼び出し側が既にトランザクションを開始したかどうかにかかわらず動作する必要があります。この主題は非常に混乱しています。ここで私はうまくいくと思うが、私はすべての分派の特定ではない。SAVE TRANSACTIONとBEGIN TRANSACTION(SQL Server)トランザクションをうまくネストする方法

事は - このStored Procedure (SP)は他人から呼ばれています。だから私は彼らがトランザクションを開始したかどうかわからない...ユーザーが私のSPを使用するトランザクションを開始する必要があっても、私はまだSave Pointsの適切な使用について質問があります...

私SPはトランザクションが進行中かどうかをテストし、そうでない場合はBEGIN TRANSACTIONで開始します。トランザクションがすでに進行中の場合は、代わりにSAVE TRANSACTION MySavePointNameでセーブポイントを作成し、これが私が行ったことを保存します。

変更を元に戻す必要がある場合は、BEGIN TRANSACTIONを先に実行した場合、ROLLBACK TRANSACTIONとなります。私がセーブポイントをしたら、ROLLBACK TRANSACTION MySavePointNameになるでしょう。このシナリオは素晴らしいと思われる。

ここで私はちょっと混乱しています。私がやった仕事を続けたいのであれば、取引を始めたらCOMMIT TRANSACTIONを実行します。しかし、もし私がセーブポイントを作ったら?私はCOMMIT TRANSACTION MySavePointNameを試してみましたが、その後、発信者は、そのトランザクションをコミットしようとするとエラーを取得:だから

The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

私は、思ったんだけど - セーブポイントがロールバックすることができます(動作します:ROLLBACK TRANSACTION MySavePointNameは、発信者のロールバックしませんがトランザクション)。しかし、おそらく誰もそれを「コミット」する必要はありませんか?元のトランザクションがコミット(またはロールバック)されると、ロールバックする必要がある場合に備えてそこにとどまります。

トランザクションを「入れ子にする」「より良い」方法がある場合は、同様にいくつかの光を放棄してください。 BEGIN TRANSACTIONをネストする方法はわかっていませんが、内部的なトランザクションをロールバックまたはコミットするだけです。 ROLLBACKは常にトップトランザクションにロールバックし、COMMITは単に@@trancountをデクリメントします。

+0

あなたの発見は答えとして掲載する価値があるかもしれません。 –

+0

@Andriy Ok、それは私がやったことです - 私の編集を削除し、代わりに答えとしてそれを使用しました。ありがとう。 –

答えて

19

私はあなたがhttp://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquot-vs-quotsave.aspx

で詳細をしたい場合、私はだから私のSP私の調査結果をも、ブログまし

...私は今、これをすべての考え出したので、私は自分の質問にお答えしますと信じて

DECLARE @startingTranCount int 
SET @startingTranCount = @@TRANCOUNT 

IF @startingTranCount > 0 
    SAVE TRANSACTION mySavePointName 
ELSE 
    BEGIN TRANSACTION 
-- … 

そして、変更をコミットする準備ができて、あなたがいる場合にのみコミットする必要があります。この、存在しない場合は、新しいトランザクションを開始しますが、1がすでに進行中である場合に保存ポイントを使用するようなもので始まります我々は自分自身で取引を開始した:

IF @startingTranCount = 0 
    COMMIT TRANSACTION 

そして最後に、ちょうどあなたの変更これまでロールバックするには:

-- Roll back changes... 
IF @startingTranCount > 0 
    ROLLBACK TRANSACTION MySavePointName 
ELSE 
    ROLLBACK TRANSACTION 
+0

'save transaction'の後に新しいネストされたトランザクションを開始しない何らかの理由はありますか? – sotn

+0

以下は、ネストトランザクションに関する記事へのリンクです。ネストされたトランザクションがコミットされると、ネストされたトランザクション数は減少しますが、何もコミットしません。したがって、外部トランザクションがコミットされている場合、すべてがロールバックされ、すべてがロールバックされます(コミットされたトランザクションを含む)。ネストされたトランザクションがロールバックを実行する場合は、自身と外部トランザクションをロールバックします。すでに存在するトランザクションに影響を与えずに独自のトランザクションのみをロールバックしたい場合は、ここで説明する手法を使用してください。 –

+0

ここにネストされたトランザクションに関する記事があります:https://technet.microsoft.com/en-us/library/ms189336%28v=sql.105%29.aspx?f=255&MSPPError=-2147217396 –

10

Brian B's answerを拡張します。

これは、セーブポイント名が一意であり、SQL Server 2012の新しいTRY/CATCH/THROW機能を使用することを保証します。

DECLARE @mark CHAR(32) = replace(newid(), '-', ''); 
DECLARE @trans INT = @@TRANCOUNT; 

IF @trans = 0 
    BEGIN TRANSACTION @mark; 
ELSE 
    SAVE TRANSACTION @mark; 

BEGIN TRY 
    -- do work here 

    IF @trans = 0 
     COMMIT TRANSACTION @mark; 
END TRY 
BEGIN CATCH 
    IF xact_state() = 1 OR (@trans = 0 AND xact_state() <> 0) ROLLBACK TRANSACTION @mark; 
    THROW; 
END CATCH 
+0

私は戻っていませんでしたこのポストにはしばらくの間、次のクエリーでちょうど再利用できるテンプレートが大好きです。ニースの強化。 –

+0

私はtryブロック内でトランザクション文を開始するMSDNの提案を見てきました。このパターンでこれを行うと、いくつかの問題が起きるか、それを行うのは完全に安全でしょうか? –

+0

catchブロックのif文について混乱します。 xact state = -1の場合にトランザクションをロールバックする理由それは<> - 1ではないでしょうか? –

2

私は私のストアドプロシージャでトランザクションマネージャのこのタイプを使用しています

CREATE PROCEDURE Ardi_Sample_Test 
     @InputCandidateID INT 
    AS 
     DECLARE @TranCounter INT; 
     SET @TranCounter = @@TRANCOUNT; 
     IF @TranCounter > 0 
      SAVE TRANSACTION ProcedureSave; 
     ELSE 
      BEGIN TRANSACTION; 
     BEGIN TRY 

      /* 
      <Your Code> 
      */ 

      IF @TranCounter = 0 
       COMMIT TRANSACTION; 
     END TRY 
     BEGIN CATCH 
      IF @TranCounter = 0 
       ROLLBACK TRANSACTION; 
      ELSE 
       IF XACT_STATE() <> -1 
        ROLLBACK TRANSACTION ProcedureSave; 

      DECLARE @ErrorMessage NVARCHAR(4000); 
      DECLARE @ErrorSeverity INT; 
      DECLARE @ErrorState INT; 
      SELECT @ErrorMessage = ERROR_MESSAGE(); 
      SELECT @ErrorSeverity = ERROR_SEVERITY(); 
      SELECT @ErrorState = ERROR_STATE(); 

      RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState); 
     END CATCH 
    GO 
関連する問題