事务逻辑和错误处理问题

时间:2020-01-29 14:40:26

标签: sql-server stored-procedures transactions

我正在遵循有关ACID概念的在线交易教程。我有这两个表:

-- Create Product table
CREATE TABLE Product
(
  ProductId INT PRIMARY KEY,
  Name VARCHAR(50),
  Price INT,
  Quantity INT
)
GO

-- Populate the Product Table with some test data
INSERT INTO Product VALUES(101, 'Laptop', 1234, 100)
INSERT INTO Product VALUES(102, 'Desktop', 3456, 150)
INSERT INTO Product VALUES(103, 'Tablet', 5678, 200)
INSERT INTO Product VALUES(104, 'Mobile', 7890, 250)
GO

-- Create ProductSales table
CREATE TABLE ProductSales
(
  ProductSalesId INT PRIMARY KEY,
  ProductId INT,
  QuantitySold INT
) 
GO

我通过交易创建了一个存储过程,在其中提供了productId和要出售的数量。

这是我的存储过程:

CREATE PROCEDURE spSellProduct
@ProductID INT,
@QuantityToSell INT
AS
BEGIN
  -- First we need to Check the stock available for the product we want to sell
  DECLARE @StockAvailable INT

  SELECT @StockAvailable = Quantity 
  FROM Product 
  WHERE ProductId = @ProductId

  -- We need to throw an error to the calling application 
  -- if the stock is less than the quantity we want to sell
  IF(@StockAvailable< @QuantityToSell)
  BEGIN
    Raiserror('Enough Stock is not available',16,1)
  END
  -- If enough stock is available
  ELSE
  BEGIN
    BEGIN TRY
      -- We need to start the transaction
      BEGIN TRANSACTION

      -- First we need to reduce the quantity available
      UPDATE    Product SET 
          Quantity = (Quantity - @QuantityToSell)
      WHERE ProductID = @ProductID

      -- Calculate MAX ProductSalesId
      DECLARE @MaxProductSalesId INT
      SELECT    @MaxProductSalesId = CASE 
          WHEN  MAX(ProductSalesId) IS NULL THEN 0 
          ELSE MAX(ProductSalesId) 
          END 
      FROM  ProductSales

      -- Increment @MaxProductSalesId by 1, so we don't get a primary key violation
      Set @MaxProductSalesId = @MaxProductSalesId + 1

      -- We need to insert the quantity sold into the ProductSales table
      INSERT INTO ProductSales(ProductSalesId, ProductId, QuantitySold)
      VALUES(@MaxProductSalesId, @ProductId, @QuantityToSell)

      COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
      ROLLBACK TRANSACTION
      SELECT    ERROR_NUMBER() as ErrorNumber,
          ERROR_MESSAGE() as ErrorMessage,
          ERROR_PROCEDURE() as ErrorProcedure,
          ERROR_STATE() as ErrorState,
          ERROR_SEVERITY() as ErrorSeverity,
          ERROR_LINE() as ErrorLine
    END CATCH
  End
END

go

spSellProduct @ProductId=103, @QuantityToSell=300

我检查数量是否少于要出售的数量,然后产生错误。 如果没有错误,那么我开始交易。

然后从产品表中扣除数量。 之后,我在productSales表中增加ProductSalesId,并使用ProductId和QuantitySold插入新行。

如果发生任何错误,我尝试在捕获批次中处理它们并回滚事务。

因此,在我的ProductId 103的Product表中,该表有200个库存,我通过以下方式执行存储过程:

spSellProduct @ ProductId = 103,@ QuantityToSell = 300

@QuantityToSell大于“数量”时,不会生成错误并且事务不会回滚。在这种情况下,ProductId的数量为200,而我尝试出售的数量为300。

我在做什么错了?

谢谢

1 个答案:

答案 0 :(得分:0)

您应该将所有过程代码放在try catch中,并在其中放入事务。尝试使用以下代码。

CREATE PROCEDURE [dbo].[spSellProduct]
@ProductID INT,
@QuantityToSell INT
AS
BEGIN
  BEGIN TRY
     BEGIN TRANSACTION 
          -- First we need to Check the stock available for the product we want to sell
          DECLARE @StockAvailable INT

          SELECT @StockAvailable = Quantity 
          FROM Product 
          WHERE ProductId = @ProductId

          -- We need to throw an error to the calling application 
          -- if the stock is less than the quantity we want to sell
          IF(@StockAvailable< @QuantityToSell)
          BEGIN
            Raiserror('Enough Stock is not available',16,1)
          END
          -- If enough stock is available
          ELSE
          BEGIN
               -- First we need to reduce the quantity available
              UPDATE    Product SET 
                  Quantity = (Quantity - @QuantityToSell)
              WHERE ProductID = @ProductID

              -- Calculate MAX ProductSalesId
              DECLARE @MaxProductSalesId INT
              SELECT    @MaxProductSalesId = CASE 
                  WHEN  MAX(ProductSalesId) IS NULL THEN 0 
                  ELSE MAX(ProductSalesId) 
                  END 
              FROM  ProductSales

              -- Increment @MaxProductSalesId by 1, so we don't get a primary key violation
              Set @MaxProductSalesId = @MaxProductSalesId + 1

              -- We need to insert the quantity sold into the ProductSales table
              INSERT INTO ProductSales(ProductSalesId, ProductId, QuantitySold)
              VALUES(@MaxProductSalesId, @ProductId, @QuantityToSell)
            END
        COMMIT TRANSACTION CHECKTR
    END TRY
    BEGIN CATCH
      ROLLBACK TRANSACTION 
      SELECT    ERROR_NUMBER() as ErrorNumber,
          ERROR_MESSAGE() as ErrorMessage,
          ERROR_PROCEDURE() as ErrorProcedure,
          ERROR_STATE() as ErrorState,
          ERROR_SEVERITY() as ErrorSeverity,
          ERROR_LINE() as ErrorLine
    END CATCH
END