¿Cuál es el patrón correcto para hacer un "UPSERT" atómico (ACTUALIZAR cuando existe, INSERTAR de lo contrario) en SQL Server 2005?UPSERT atómico en SQL Server 2005
I ver una gran cantidad de código en SO (por ejemplo, véase Check if a row exists, otherwise insert) con el siguiente patrón de dos partes:
UPDATE ...
FROM ...
WHERE <condition>
-- race condition risk here
IF @@ROWCOUNT = 0
INSERT ...
o
IF (SELECT COUNT(*) FROM ... WHERE <condition>) = 0
-- race condition risk here
INSERT ...
ELSE
UPDATE ...
donde < condición> será una evaluación de los recursos naturales llaves. Ninguno de los enfoques anteriores parece tratar bien con la concurrencia. Si no puedo tener dos filas con la misma clave natural, parece que todo el riesgo anterior inserta filas con las mismas claves naturales en los escenarios de condición de carrera.
He estado usando el siguiente enfoque pero me sorprende no ver en cualquier lugar de respuestas de la gente por lo que me pregunto lo que está mal con él:
INSERT INTO <table>
SELECT <natural keys>, <other stuff...>
FROM <table>
WHERE NOT EXISTS
-- race condition risk here?
(SELECT 1 FROM <table> WHERE <natural keys>)
UPDATE ...
WHERE <natural keys>
Tenga en cuenta que la condición de carrera es mencionado aquí una diferente de las del código anterior. En el código anterior, el problema eran las lecturas fantasmas (filas que se insertan entre UPDATE/IF o entre SELECT/INSERT por otra sesión). En el código anterior, la condición de carrera tiene que ver con DELETE. ¿Es posible que una sesión coincidente sea eliminada por otra sesión DESPUÉS de que se ejecute (DONDE NO EXISTE) pero antes de que se ejecute INSERT? No está claro dónde DONDE NO EXISTE pone un candado en algo junto con la ACTUALIZACIÓN.
¿Esto es atómico? No puedo encontrar dónde se documentará esto en la documentación de SQL Server.
EDIT: Me doy cuenta de que esto se puede hacer con las transacciones, pero creo que tendría que establecer el nivel de transacción en SERIALIZABLE para evitar el problema de lectura fantasma? Seguramente eso es exagerado para un problema tan común?
Mladen Prajdić tiene un artículo aquí que puede ser interesante. http://www.sqlteam.com/article/application-locks-or-mutexes-in-sql-server-2005 y aquí http://weblogs.sqlteam.com/mladenp/archive/2007/07/30/60273 .aspx –
El *** patrón *** correcto para * cualquier * solicitud que implique la palabra "Atomic" y más de una instrucción SQL debe * siempre * estar obligado con BEGIN TRANSACTION y COMMIT/ROLLBACK. – RBarryYoung