トランザクションのセーブポイント

セーブポイントを使用すると、トランザクションを部分的にロールバックできます。セーブポイントは、SAVE TRANSACTION savepoint_name ステートメントを使用して作成できます。セーブポイントを作成すると、ROLLBACK TRANSACTION savepoint_name ステートメントを実行して、トランザクションの最初ではなく、セーブポイントにロールバックできます。

セーブポイントは、エラーの発生率が低い場合に役立ちます。エラーの発生率が低い場合は、セーブポイントを使用してトランザクションの一部をロールバックする方が、トランザクションごとに更新が有効かどうかを確認してから更新するよりも効率的です。更新操作もロールバック操作もリソースを多く消費するので、エラーの発生率が低く事前に更新の妥当性を評価するコストが高い場合に限り、セーブポイントが効果的です。

次に、企業が有能な仕入れ先と発注水準を確保していて在庫切れの可能性が低い注文システムで、セーブポイントを使用した場合の例を示します。通常、アプリケーションは注文を計上するための更新を行う前に、在庫量が十分であることを確認します。この例では、何らかの理由により (使用するモデムや WAN が低速であるなど)、在庫量を事前に確認するために比較的高いコストを要するとします。この場合、アプリケーションでは更新だけを行い、在庫不足エラーが返された場合には更新をロールバックすることができます。更新後に @@ERROR を短時間で確認する方が、更新前に在庫量を確認するよりはるかに効率的です。

InvCtrl テーブルには、QtyInStk 列が 0 未満になると、エラー 547 を生成する CHECK 制約があります。OrderStock プロシージャによりセーブポイントが作成されます。エラー 547 が発生した場合、このプロシージャはそのセーブポイントまでロールバックし、在庫数量を呼び出し側プロセスに返します。呼び出し側プロセスでは、注文を在庫数量分に変更できます。OrderStock から 0 が返された場合、呼び出し側プロセスは注文に応じられるだけの在庫があったことを確認できます。

SET NOCOUNT OFF;
GO
USE AdventureWorks2008R2;
GO
CREATE TABLE InvCtrl
    (WhrhousID      int,
    PartNmbr      int,
    QtyInStk      int,
    ReordrPt      int,
    CONSTRAINT InvPK PRIMARY KEY
    (WhrhousID, PartNmbr),
    CONSTRAINT QtyStkCheck CHECK (QtyInStk > 0) );
GO
CREATE PROCEDURE OrderStock
    @WhrhousID int,
    @PartNmbr int,
    @OrderQty int
AS
    DECLARE @ErrorVar int;
    SAVE TRANSACTION StkOrdTrn;
    UPDATE InvCtrl SET QtyInStk = QtyInStk - @OrderQty
        WHERE WhrhousID = @WhrhousID
        AND PartNmbr = @PartNmbr;
    SELECT @ErrorVar = @@error;
    IF (@ErrorVar = 547)
    BEGIN
        ROLLBACK TRANSACTION StkOrdTrn;
        RETURN (SELECT QtyInStk
                FROM InvCtrl
                WHERE WhrhousID = @WhrhousID
                AND PartNmbr = @PartNmbr);
    END
    ELSE
        RETURN 0;
GO