在同一时间发生的事务会产生错误的选择结果



我有一个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表。

我需要这个选择来获得绝对的电流量。

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

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

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

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

2( 使用sp_getapplock创建单个用户代码块。

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

最新更新