2010-02-19 24 views
5

Esta es una explicación demasiado simplificada de lo que estoy trabajando.
Tengo una tabla con la columna de estado. Varias instancias de la aplicación extraerán los contenidos de la primera fila con un estado de NEW, actualizarán el estado a WORKING y luego irán a trabajar en los contenidos.
Es bastante fácil hacer esto con dos llamadas a la base de datos; primero el SELECT luego el UPDATE. Pero quiero hacerlo todo en una llamada para que otra instancia de la aplicación no saque la misma fila. Algo así como una cosa SELECT_AND_UPDATE.¿Puedo seleccionar y actualizar al mismo tiempo?

¿Es un procedimiento almacenado el mejor camino a seguir?

Respuesta

2

Suena como un escenario de procesamiento de cola, en el que desea un proceso solo para recoger un registro determinado.

Si ese es el caso, echar un vistazo a la respuesta que he dado el día de hoy, que describe cómo implementar esta lógica utilizando una transacción en conjunto con UPDLOCK y READPAST sugerencias de tabla: Row locks - manually using them

Mejor envuelto en sproc .

No estoy seguro de que esto es lo que quieres hacer, por lo tanto, no he votado para cerrarlo como duplicado.

+0

Esto es lo que necesitaba. Muchas gracias. –

1

No del todo, pero puede SELECT ... WITH (UPDLOCK), luego UPDATE.. posteriormente. Esto es tan bueno como una operación atómica ya que le dice a la base de datos que está a punto de actualizar lo que seleccionó previamente, por lo que puede bloquear esas filas, evitando colisiones con otros clientes. Bajo Oracle y alguna otra base de datos (creo que MySQL) la sintaxis es SELECT ... FOR UPDATE.

Nota: Creo que deberá asegurarse de que las dos declaraciones sucedan dentro de una transacción para que funcione.

+0

no debería ser ... CON UPDLOCK para Sql Server.Además, tenga cuidado porque la sugerencia de UPDLOCK significa que los bloqueos se mantienen contra sus datos, lo que en una transacción de larga ejecución podría causar desacuerdo con otras transacciones –

+0

@Jeff: Sí, pero según su descripción, esto es lo que quería. De hecho, debe tener cuidado con las transacciones largas, pero es la única manera que conozco de obtener un comportamiento atómico de tipo "leer y escribir". – jkp

8

Puede usar la instrucción OUTPUT.

DECLARE @Table TABLE (ID INTEGER, Status VARCHAR(32)) 
INSERT INTO @Table VALUES (1, 'New') 
INSERT INTO @Table VALUES (2, 'New') 
INSERT INTO @Table VALUES (3, 'Working') 

UPDATE @Table 
SET  Status = 'Working' 
OUTPUT Inserted.* 
FROM @Table t1 
     INNER JOIN (
      SELECT TOP 1 ID 
      FROM @Table 
      WHERE Status = 'New' 
     ) t2 ON t2.ID = t1.ID 
+0

Tenga en cuenta que esto requiere SQL Server 2005 o posterior. – AakashM

+0

Creí que el autor quería hacer un trabajo adicional dentro de la transacción, sin embargo, nada en la pregunta lo confirma. Entonces un +1. – Quassnoi

+0

Por cierto, no se 'unirá' aquí, un 'CTE' será suficiente. – Quassnoi

1

Usted debe hacer tres cosas aquí:

  1. Bloquear la fila que está trabajando
  2. Asegúrese de que esta y sólo esta fila está bloqueada
  3. no espere a que la posición de bloqueo registros: omita los siguientes en su lugar.

Para ello, sólo ejecuta lo siguiente:

SELECT TOP 1 * 
FROM mytable (ROWLOCK, UPDLOCK, READPAST) 
WHERE status = 'NEW' 
ORDER BY 
     date 

UPDATE … 

dentro de una transacción.

1

Un procedimiento almacenado es el camino a seguir. Necesitas ver las transacciones. El servidor Sql nació para este tipo de cosas.

1

Sí, y tal vez utilice la sugerencia de rowlock para mantenerlo aislado de los otros hilos, por ej.

UPDATE 
Jobs WITH (ROWLOCK, UPDLOCK, READPAST) 
SET Status = 'WORKING' 
WHERE JobID = 
(SELECT Top 1 JobId FROM Jobs WHERE Status = 'NEW') 

EDIT: Rowlock sería mejor como lo sugiere Quassnoi, pero la misma idea se aplica para hacer la actualización en una consulta.

+0

¿No irían los consejos en el SELECCIONAR? –

+0

No creo que marque la diferencia ya que solo una fila está en juego aquí y está en una declaración. – Turnkey

Cuestiones relacionadas