19

特定の状況下で、実行したすべてを元に戻してエラーコードを呼び出し元に返すか、受け入れ/コミットして成功を呼び出し元に返すことができるように、セーブポイントを設定する必要があるストアドプロシージャがあります。しかし、発信者がすでにトランザクションを開始しているかどうかに関係なく、動作する必要があります。ドキュメントは、このテーマに関して非常に混乱しています。これがうまくいくと思うことですが、すべての影響については確信がありません。

問題は、これStored Procedure (SP)は他の人から呼ばれているということです。そのため、ユーザーがトランザクションを開始したかどうかはわかりません...ユーザーにSPを使用するためにトランザクションを開始するように要求した場合でも、Save Points...の適切な使用について質問があります。

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

次に、変更をロールバックする必要がある場合、BEGIN TRANSACTION以前に行った場合は、ロールバックしますROLLBACK TRANSACTION。セーブポイントを実行した場合は、実行しますROLLBACK TRANSACTION MySavePointName。このシナリオはうまく機能しているようです。

ここで少し混乱します。これまでに行った作業を維持したい場合は、トランザクションを開始した場合に実行しCOMMIT TRANSACTIONます。しかし、セーブポイントを作成した場合はどうなりますか?試しCOMMIT TRANSACTION MySavePointNameましたが、呼び出し元がトランザクションをコミットしようとしてエラーが発生します。

COMMIT TRANSACTION要求には、対応するBEGINTRANSACTIONがありません。

だから私は疑問に思っています-セーブポイントはロールバックできます(それは機能します:ROLLBACK TRANSACTION MySavePointName呼び出し元のトランザクションをロールバックしません)。しかし、おそらくそれを「コミット」する必要はないのでしょうか。ロールバックする必要がある場合に備えて、そのまま残りますが、元のトランザクションがコミット(またはロールバック)されると消えますか?

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

4

3 に答える 3

28

私はこれをすべて理解したと信じているので、私自身の質問に答えます...

詳細については、 http://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquo​​t-vs-quotsaveをご覧ください。 .aspx

したがって、私のSPは次のよ​​うなものから始まり、トランザクションがない場合は新しいトランザクションを開始しますが、すでに進行中の場合はセーブポイントを使用します。

DECLARE @startingTranCount int
SET @startingTranCount = @@TRANCOUNT

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

次に、変更をコミットする準備ができたら、トランザクションを自分で開始した場合にのみコミットする必要があります。

IF @startingTranCount = 0
    COMMIT TRANSACTION

そして最後に、これまでの変更だけをロールバックするには:

-- Roll back changes...
IF @startingTranCount > 0
    ROLLBACK TRANSACTION MySavePointName
ELSE
    ROLLBACK TRANSACTION
于 2012-03-16T21:11:43.480 に答える
15

ブライアンBの答えを拡張します。

これにより、保存ポイント名が一意になり、SQLServer2012の新しい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
于 2014-10-14T16:00:11.757 に答える
4

ストアドプロシージャでこのタイプのトランザクションマネージャーを使用しました:

    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  
于 2017-02-03T19:16:07.280 に答える