2009-09-26 8 views
7

Quiero actualizar un solo registro en una tabla para reflejar que una sesión de cliente determinada ha adquirido el registro (y ahora lo posee para más actualizaciones) dentro de un entorno de sesiones múltiples. Tengo esto hasta ahora:Actualización de registro atómico exclusivo de SQL

create procedure AcquireRow(
    @itemNo int,   -- Item ID to acquire 
    @sessNo int,   -- Session ID 
    @res  char(1) out) -- Result 
as 
begin 
    -- Attempt to acquire the row 
    update Items 
    set 
     State = 'A',  -- 'A'=Acquired 
     SessionID = @sessNo 
    where ItemID = @itemNo 
     and State = 'N'; -- 'N'=Not acquired 

    -- Verify that the session actually acquired the row 
    set @res = 'T';  -- 'T'=Success 
    if @@rowcount = 0 
    set @res = 'F';  -- 'F'=Failure 
end; 

La variable de salida se establece en @state'T' si el procedimiento ha adquirido con éxito la fila, de lo contrario se configura en 'F' para indicar el fracaso.

Mi pregunta: ¿Es esta garantizado para trabajar de forma atómica, por lo que sólo adquiere una sesión con éxito (actualizaciones) la fila si varias sesiones llaman AcquireRow() al mismo tiempo? ¿O hay una mejor manera de hacer esto? ¿Necesito un rowlock explícito?

modificada:
Basado en la respuesta de Remus, me reorganizar el código así:

set @res = 'F'; 
update ...; 
if @@rowcount > 0 
    set @res = 'T'; 

Uso de una cláusula de output o asignar de ItemID la fila resultante a una variable dentro de la update también sería prudente.

+0

Una pregunta muy similar ha sido formulada en SO, pero no puedo encontrarla ... –

+0

Esta es similar pero no la que tenía en mente: http://stackoverflow.com/questions/574549/ efficient-transaction-record-locking –

Respuesta

9

@@ROWCOUNT es notoriamente fácil equivocarse. Por ejemplo, en tu caso. De MSDN:

declaraciones que hacen un simple asignación siempre se establece el valor @@ROWCOUNT a 1. No hay filas se envían al cliente . Ejemplos de estas declaraciones son: SET @local_variable ...

ser correcta debe comprobar el @@ ROWCOUNT inmediatamente después de la UPDATE. Es más seguro usar un ROWLOCK pero no es necesario. Incluso si el optimizador decide usar bloqueos de página, la semántica 'adquirir' es correcta.

que probablemente preferirían otro enfoque, que consiste en utilizar el OUTPUT cláusula de UPDATE:

declare @updated table (ItemId int); 

update Items 
set ... 
output inserted.ItemId 
into @updated (ItemId) 
where ... 

Este esquema es más a prueba de errores y también más flexible, ya que permite la adquisición de ItemId desconocido: el id adquirió se coloca en la variable de tabla @updated y se puede devolver a la persona que llama.

Como nota general, el uso de actualizaciones reales y comprometidas para 'adquirir' está plagado de problemas ya que no puede saber qué filas realmente se adquieren y cuáles se acaban de abandonar (cliente desconectado o bloqueado sin liberar la 'adquisición ').

+0

Gracias, no sabía sobre ese peligro oculto particular de '@@ rowcount'. En una versión anterior de mi código original, configuré '@ res' antes de' update', por lo que no habría tenido ese problema. También consideré usar 'output' o' set @updateID = ItemID = @ itemNo' para capturar la identificación de la fila afectada, pero no estaba seguro de ello. Gracias por la información. –

3

Las instrucciones UPDATE en SQL Server adquieren un bloqueo de actualización mientras el motor de la base de datos lee las filas que necesitan actualización, que se convierte en un bloqueo exclusivo cuando se produce la escritura.

Cuando hay un bloqueo exclusivo en una fila, todas las demás transacciones están bloqueadas para leerlo y escribirlo (a menos que la transacción de lectura esté en el aislamiento LEÍDO NO COMUNITADO o se utiliza una sugerencia NOLOCK).

Así que sí, tal como está, su instrucción UPDATE es una transacción autocommitida atómica, por lo que esto debería estar bien con respecto a varias sesiones llamándolo al mismo tiempo. Si tuviera que dividirlo en varias declaraciones por algún motivo, debería asegurarse de crear explícitamente una transacción en su SP.

Los comentarios de Remus con respecto a @@ ROWCOUNT y el uso general de "adquirir" son un consejo bastante sólido.

+0

Gran actualización: ¿tiene alguna referencia a MSDN o similar para esto? –

Cuestiones relacionadas