繁体   English   中英

存储过程中的SQL Server事务

[英]SQL Server Transaction inside stored procedure

假设我有两个表ProductProductSales

Product表有:

ProductID(int)(pk)
Name(varchar2)
UnitPrice(float) 
ProductAvailable(int)

ProductSales表具有:

ProductSalesID(int)(pk)
ProductID(int)
Quantity(int)

我想在存储过程中创建一个事务,该事务首先检查Quantity是否小于ProductAvailable 如果更大,则Rollback事务,否则从ProductAvailable扣除Quantity (由用户提供),并插入到ProductSales表中。 我如何使用SQL Server解决方案

并发更新的解决方案:

-- Test Data
CREATE TABLE Product (
    ProductID int NOT NULL IDENTITY PRIMARY KEY CLUSTERED
    ,Name varchar(256) NOT NULL
    ,UnitPrice money NOT NULL
    ,ProductAvailable int NOT NULL)

CREATE TABLE ProductSales (
    ProductSalesID int NOT NULL IDENTITY PRIMARY KEY CLUSTERED
    ,ProductID int NOT NULL
    ,Quantity int NOT NULL)

INSERT INTO Product (Name, UnitPrice, ProductAvailable)
VALUES ('Prod1', 5.0, 10)
    ,('Prod2',6.0, 5)
GO

第一个变体-MSDN OUTPUT子句

CREATE PROCEDURE Sale 
    @ProductID int
    ,@Quantity int
AS
BEGIN
    IF @Quantity <= 0 RETURN -1;

    -- atomic operation
    UPDATE Product
        SET ProductAvailable = ProductAvailable - @Quantity
    OUTPUT INSERTED.ProductID, @Quantity INTO ProductSales (ProductID, Quantity)
    WHERE ProductID = @ProductID
        AND ProductAvailable >= @Quantity;

    IF @@ROWCOUNT = 0 RETURN -1;
END
GO

但是它有局限性-output_table不能

  • 已启用已定义的触发器。
  • 参与FOREIGN KEY约束的任一侧。
  • 有检查约束或启用的规则。

第二种变体-MSDN交易声明

CREATE PROCEDURE Sale2
    @ProductID int
    ,@Quantity int
AS
BEGIN
    DECLARE @ProcID CHAR(36) = NEWID()
            ,@TranCount Int = @@TranCount
            ,@ProductAvailable int
            ,@errorMessage nvarchar(4000)
            ,@errorSeverity int
            ,@errorState int
            ,@ReturnCode int = -1;

    IF @Quantity <= 0 RETURN -1;

    IF (@TranCount = 0)
        BEGIN TRANSACTION;
    ELSE
        SAVE TRANSACTION @ProcID;

    BEGIN TRY
        SELECT @ProductAvailable = ProductAvailable 
        FROM Product WITH (XLOCK, HOLDLOCK)
        WHERE ProductID = @ProductID;

        -- Record is locked, you can do any checks here
        IF @ProductAvailable >= @Quantity
            BEGIN
                UPDATE Product
                    SET ProductAvailable = ProductAvailable - @Quantity
                WHERE ProductID = @ProductID;

                INSERT INTO ProductSales (ProductID, Quantity)
                    VALUES(@ProductID, @Quantity);

                SET @ReturnCode  = 1;
            END
        ELSE
            SET @ReturnCode  = -1;

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

        SELECT @errorMessage = 'Error in [Sale2]: ' + ERROR_MESSAGE(), @errorSeverity = ERROR_SEVERITY(), @errorState = ERROR_STATE();
        RAISERROR (@errorMessage, @errorSeverity, @errorState);
        SET @ReturnCode  = -1;
    END CATCH

    RETURN @ReturnCode;
END
GO

检查结果

-- Test
DECLARE @RetCode int

EXEC @RetCode = Sale @ProductID = 1, @Quantity = 7
SELECT @RetCode as RetCode

EXEC @RetCode = Sale2 @ProductID = 1, @Quantity = 2
SELECT @RetCode as RetCode

EXEC @RetCode = Sale @ProductID = 2, @Quantity = 7
SELECT @RetCode as RetCode
GO
SELECT * FROM ProductSales
SELECT * FROM Product
GO

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM