Sql server 如何使用锁定提示,使两个并行查询返回不相交的结果?

Sql server 如何使用锁定提示,使两个并行查询返回不相交的结果?,sql-server,database,sql-server-2005,tsql,concurrency,Sql Server,Database,Sql Server 2005,Tsql,Concurrency,我有一个SQL表Tasks,其中包含列Id和State。我需要执行以下操作:查找状态为ReadyForProcessing的任何一个任务,检索其所有列并将其状态设置为Processing。类似于(伪代码): 此查询将从多个数据库客户端并行运行,其思想是,如果两个客户端并行运行查询,它们将获得不同的任务,而不是相同的任务 看起来我需要锁定提示。我读过,但什么都不懂。如何使用锁定提示来解决上述问题 像这样的事情怎么样: UPDATE TOP (1) Tasks SET State = Pr

我有一个SQL表
Tasks
,其中包含列
Id
State
。我需要执行以下操作:查找状态为
ReadyForProcessing
的任何一个任务,检索其所有列并将其状态设置为
Processing
。类似于(伪代码):

此查询将从多个数据库客户端并行运行,其思想是,如果两个客户端并行运行查询,它们将获得不同的任务,而不是相同的任务


看起来我需要锁定提示。我读过,但什么都不懂。如何使用锁定提示来解决上述问题

像这样的事情怎么样:

UPDATE TOP (1) Tasks 
    SET State = Processing 
    OUTPUT INSERTED.RetrievedTaskId 
    WHERE State = ReadyForProcessing 
测试一下:

DECLARE @Tasks table (RetrievedTaskId  int, State char(1))
INSERT @Tasks VALUES (1,'P')
INSERT @Tasks VALUES (2,'P')
INSERT @Tasks VALUES (3,'R')
INSERT @Tasks VALUES (4,'R')

UPDATE TOP (1) @Tasks
  SET State = 'P'
  OUTPUT INSERTED.RetrievedTaskId
  WHERE State = 'R'

SELECT * FROM @Tasks
--输出:


我真的,真的不喜欢数据库中的显式锁定,它是各种疯狂错误的来源——数据库的性能可能会下降

我建议按照以下思路重新编写SQL:

begin transaction;

update tasks
set state = processing
where state = readyForProcessing
and ID = (select min(ID) from tasks where state = readyForProcessing);

commit; 

这样,您就不需要锁定任何东西,而且因为更新是原子的,所以没有两个进程更新同一条记录的风险。

这应该可以做到

BEGIN TRANSACTION
DECLARE @taskId
SELECT TOP (1) @taskid = TaskId FROM Tasks WITH (UPDLOCK, READPAST) WHERE State = 'ReadyForProcessing' 
UPDATE Tasks SET State = 'Processing' WHERE TaskId = @taskid
COMMIT TRAN

请参阅和。那么,如何处理作为原子操作检索任务列的需求呢?这正是显式锁定所针对的用例类型。确保不要在SERIALIZABLE下运行它-您可能会遇到转换死锁。在更新之前执行SELECT时,会出现竞争,因此两个线程完全有可能两次更新同一条记录(这可能不是问题,因为他们会将记录更新为相同的值,但我已经看到了这种情况的发生)。处理这种情况的唯一方法是使用显式锁定提示。
begin transaction;

update tasks
set state = processing
where state = readyForProcessing
and ID = (select min(ID) from tasks where state = readyForProcessing);

commit; 
BEGIN TRANSACTION
DECLARE @taskId
SELECT TOP (1) @taskid = TaskId FROM Tasks WITH (UPDLOCK, READPAST) WHERE State = 'ReadyForProcessing' 
UPDATE Tasks SET State = 'Processing' WHERE TaskId = @taskid
COMMIT TRAN