sql server - sirve - ¿Cómo uso las sugerencias de bloqueo para que dos consultas paralelas devuelvan resultados no intersecantes?
sql server with nolock español (3)
¿Qué tal algo así?
UPDATE TOP (1) Tasks
SET State = Processing
OUTPUT INSERTED.RetrievedTaskId
WHERE State = ReadyForProcessing
pruébalo:
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
--SALIDA:
RetrievedTaskId
---------------
3
(1 row(s) affected)
RetrievedTaskId State
--------------- -----
1 P
2 P
3 P
4 R
(4 row(s) affected)
Tengo una tabla SQL Tasks
con columnas Id
y State
. Necesito hacer lo siguiente: encontrar cualquier tarea con estado ReadyForProcessing
, recuperar todas sus columnas y establecer su estado en Processing
. Algo así como (pseudocódigo):
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
Esta consulta se ejecutará en paralelo desde varios clientes de bases de datos y la idea es que si dos clientes ejecutan la consulta en paralelo adquieren tareas diferentes y nunca la misma tarea.
Parece que necesito pistas de bloqueo. He leído este artículo de MSDN pero no entiendo nada allí. ¿Cómo uso los consejos de bloqueo para resolver el problema anterior?
Esto debería funcionar.
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
Realmente, realmente no me gusta el bloqueo explícito en las bases de datos, es una fuente de todo tipo de errores locos, y el rendimiento de la base de datos puede caer por el suelo.
Sugeriría que se vuelva a escribir el SQL en las siguientes líneas:
begin transaction;
update tasks
set state = processing
where state = readyForProcessing
and ID = (select min(ID) from tasks where state = readyForProcessing);
commit;
De esta manera, no necesita bloquear nada, y dado que la actualización es atómica, no hay riesgo de que dos procesos actualicen el mismo registro.