我有一个包含Tasks
和Id
列的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但不明白。如何使用锁定提示解决上述问题?
答案 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;
这样,您不需要锁定任何东西 - 并且由于更新是原子的,因此不存在两个进程更新同一记录的风险。