首页 > 解决方案 > 同时发生的事务会产生错误的选择结果

问题描述

我有一个 SQL 数据库来跟踪库存中的项目。物品有数量,可以签出。

我有一个包含 OnHandQty 和 OutQty的项目表。

每当发生结帐时,我都会在我的ItemHistory表中存储一条记录并更新我的Items表中的数量值以反映更改。

在同时将大量事务发送到服务器之前,这非常有效。

我的结帐查询如下所示:

BEGIN TRY
BEGIN TRANSACTION 

    DECLARE @OnHand [int]
    DECLARE @Out [int]

    SELECT @OnHand = QtyOnHand, @Out = QtyOut
    FROM [Item] 

    IF (((-@qty) + @OnHand) >= 0)
        BEGIN
            INSERT INTO [dbo].[ItemHistory] (...)
            OUTPUT Inserted.ItemID
            VALUES(...)

            UPDATE [Item]
            SET 
            QtyOnHand = @OnHand - @qty,
            QtyOut = @Out + @qty
        END
    ELSE
        BEGIN
            ROLLBACK TRAN
        END
COMMIT
END TRY
BEGIN CATCH
   IF @@TRANCOUNT > 0
      ROLLBACK TRAN
END CATCH

问题出现在这里:

SELECT @OnHand = QtyOnHand, @Out = QtyOut
FROM [Item] 

服务器有时会同时开始两个事务。这会导致@OnHand@Out变量在两个单独的事务中完全相同。这不好,因为这些应该代表当前的总数。他们在技术上是这样做的,因为此时服务器还没有时间更新Item表。

我需要这个选择来获得绝对电流量以使其工作。

有没有办法防止交易同时发生?或者有没有更好的方法来处理这个过程?

标签: sqlsql-servertsql

解决方案


在 SQL Server 中处理并发问题的两种可能方法是:

1)在表上获取排他表锁with (tablockx),例如

SELECT @OnHand = QtyOnHand, @Out = QtyOut FROM [Item] WITH (tablockx)

2)sp_getapplock用于创建单个用户代码块。

注意:请记住,强制单用户访问此代码块会降低数据库的性能吞吐量。所以你想让这些锁尽可能短。


推荐阅读