事务保存点

保存点提供了一种机制,用于回滚部分事务。您可以使用 SAVE TRANSACTION savepoint_name 语句创建保存点。然后执行 ROLLBACK TRANSACTION savepoint_name 语句以回滚到保存点,而不是回滚到事务的起点。

在不可能发生错误的情况下,保存点很有用。在很少出现错误的情况下使用保存点回滚部分事务,比让每个事务在更新之前测试更新的有效性更为有效。更新和回滚操作代价很大,因此只有在遇到错误的可能性很小,而且预先检查更新的有效性的代价相对很高的情况下,使用保存点才会非常有效。

下面的示例说明了保存点在一个订单系统中的使用情况。该系统中存货不足的可能性很小,因为该公司具备有效的供应商和分购点。通常应用程序在尝试更新订单记录时,会先验证目前是否有足够的存货。下面的示例假定由于某种原因,验证目前可用存货量代价相对较大(由于连接到一个低速的调制解调器或广域网上)。可将应用程序编写为只进行更新,而且如果收到错误信息表明库存不足时,将回滚该更新。在这种情况下,在插入之后快速检查 @@ERROR 要比在更新之前验证库存数量的速度要快得多。

InvCtrl 表有一个 CHECK 约束,如果 QtyInStk 列小于 0,它就会触发 547 错误。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