Digamos que tiene el siguiente código (ignore que es horrible):
BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else
En mi opinión, esto NO está gestionando la concurrencia correctamente. El hecho de que tenga una transacción no significa que otra persona no leerá el mismo valor que usted antes de llegar a su estado de actualización.
Ahora, dejando el código como está (me doy cuenta de que esto se maneja mejor como una sola declaración o incluso mejor usando una columna de autoincremento / identidad), ¿cuáles son formas seguras de hacer que maneje la concurrencia correctamente y evite las condiciones de carrera que permiten que dos clientes obtengan lo mismo? valor de identificación?
Estoy bastante seguro de que agregar un WITH (UPDLOCK, HOLDLOCK)
a SELECT hará el truco. El nivel de aislamiento de la transacción SERIALIZABLE parece funcionar también, ya que niega a otra persona que lea lo que hizo hasta que termine el tran ( ACTUALIZACIÓN : esto es falso. Vea la respuesta de Martin). ¿Es eso cierto? ¿Funcionarán ambos igualmente bien? ¿Se prefiere uno sobre el otro?
Imagine hacer algo más legítimo que una actualización de ID: algunos cálculos basados en una lectura que necesita actualizar. Podría haber muchas tablas involucradas, algunas de las cuales escribirás y otras no. ¿Cuál es la mejor práctica aquí?
Después de escribir esta pregunta, creo que las sugerencias de bloqueo son mejores porque entonces solo está bloqueando las tablas que necesita, pero agradecería el aporte de cualquiera.
PD: ¡No, no sé la mejor respuesta y realmente quiero entenderlo mejor! :)
update
que pueden estar basados en datos obsoletos? Si es esto último, puede usar larowversion
columna para verificar si la fila a actualizar no ha cambiado desde que se leyó.