如何使用锁定提示,以便两个并行查询返回非交叉结果?

时间:2011-03-25 14:09:39

标签: sql-server database sql-server-2005 tsql concurrency

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

BEGIN TRANSACTION;
SELECT TOP 1 * FROM Tasks WHERE State = ReadyForProcessing
// here check if the result set is not empty and get the id, then
UPDATE Tasks SET State = Processing WHERE TaskId = RetrievedTaskId
END TRANSACTION

此查询将从多个数据库客户端并行运行,其想法是,如果两个客户端并行运行查询,则会获取不同的任务,并且从不执​​行相同的任务。

看起来我需要锁定提示。我读过this MSDN article但不明白。如何使用锁定提示解决上述问题?

3 个答案:

答案 0 :(得分:2)

这应该可以解决问题。

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

答案 1 :(得分:1)

这样的事情:

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

- 输出:

RetrievedTaskId
---------------
3

(1 row(s) affected)

RetrievedTaskId State
--------------- -----
1               P
2               P
3               P
4               R

(4 row(s) affected)

答案 2 :(得分:0)

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

我建议按以下方式重写SQL:

begin transaction;

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

commit; 

这样,您不需要锁定任何东西 - 并且由于更新是原子的,因此不存在两个进程更新同一记录的风险。