Savepoints offer a mechanism to roll back portions of transactions. You create a savepoint using the SAVE TRANSACTION savepoint_name statement, then later execute a ROLLBACK TRANSACTION savepoint_name statement to roll back to the savepoint instead of rolling back to the start of a transaction.
Savepoints are useful in situations where errors are unlikely to occur. The use of a savepoint to roll back part of a transaction in the case of an infrequent error can be more efficient than having each transaction test to see if an update is valid before making the update. Updates and rollbacks are expensive operations, so savepoints are effective only if the probability of encountering the error is low and the cost of checking the validity of an update beforehand is relatively high.
This example shows the use of a savepoint in an order system in which there is a low probability of running out of stock because the company has effective suppliers and reorder points. Usually an application would verify that there is enough stock on hand before attempting to make the updates that would record the order. This example assumes that, for some reason (such as connecting over a slow modem or WAN), first verifying the quantity of stock available is relatively expensive. The application could be coded to just make the update, and if it gets an error indicating that there is not enough stock, it rolls back the update. In this case, a quick check of @@ERROR after the insert is much faster than verifying the amount before the update.
The InvCtrl table has a CHECK constraint that triggers a 547 error if the QtyInStk column goes below 0. The OrderStock procedure creates a savepoint. If a 547 error occurs, it rolls back to the savepoint and returns the number of items on hand to the calling process. The calling process can then decide if it wants to replace the order for the quantity on hand. If OrderStock returns a 0, the calling process knows there was enough stock on hand to satisfy the order.
SET NOCOUNT OFF
GO
USE pubs
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 = 1
AND PartNmbr = 1
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
ROLLBACK TRANSACTION | SAVE TRANSACTION |