我使用表作为队列来处理记录。我使用此查询,有时会返回相同的 ID,而这种情况永远不会发生。我已经审查了几乎所有内容,但找不到它为不同工作人员返回相同 ID 的原因。
在这种情况下,我有 8 个工作人员检索下一批要处理的数据,偶尔我会收到一条错误消息,表明该批次已被处理。我的理解是,对于运行相同查询的不同工作人员来说,查询返回的 ID 永远不应该相同。
请多指教!
BEGIN TRANSACTION
UPDATE TOP(1)
tblBatch WITH (ROWLOCK, UPDLOCK, READPAST)
SET
BatchStatusID = 2
OUTPUT
inserted.BatchID
FROM
tblBatch
WHERE
tblBatch.BatchID IN (
SELECT
tblBatch.BatchID
FROM
tblBatch
INNER JOIN tblBatchName ON tblBatch.BatchID = tblBatchName.BatchID
WHERE
(tblBatch.BatchStatusID = 1)
GROUP BY
tblBatch.BatchID
HAVING
COUNT(tblBatchName.BatchID) >= 1 AND COUNT(tblBatchName.BatchID) <= 2147483647
)
COMMIT
除了 UPDLOCK 和 READPAST 之外,我还添加了 ROWLOCK 并使用事务。这没有什么区别。
您的主要问题是内部子查询引用
tblBatch
also需要锁定提示WITH (ROWLOCK, UPDLOCK, READPAST)
。
您还需要确保它将直接锁定在正确的行上。你需要一个索引如下
tblBatch (BatchStatusID, BatchID)
但是看起来您可能最好使用窗口函数并直接更新子查询。
此外,该连接似乎没有执行任何操作,因此您可以将其删除。
UPDATE TOP (1)
b
SET
BatchStatusID = 2
OUTPUT
inserted.BatchID
FROM
(
SELECT
b.*,
COUNT(*) OVER (PARTITION BY b.BatchID) AS Count
FROM
tblBatch b WITH (ROWLOCK, UPDLOCK, READPAST)
WHERE
b.BatchStatusID = 1
) b
WHERE
b.count BETWEEN 1 AND 2147483647;
但是无论如何这都没有意义。假设
BatchId
是主键,则计数将始终恰好是 1
。并且 COUNT
无论如何都不能为零,并且不能大于 int
值的最大值,因此计数检查是没有意义的。
所以你不妨忘记它并使用单级更新。
UPDATE TOP(1)
b
SET
BatchStatusID = 2
OUTPUT
inserted.BatchID
FROM
tblBatch b WITH (ROWLOCK, UPDLOCK, READPAST)
WHERE
tblBatch.BatchStatusID = 1;
请注意,显式事务不是必需的:无论如何,单个语句都是原子的。