Mucha gente le sugerirá que use MERGE
, pero le advierto que no lo haga. Por defecto, no lo protege de concurrencia y condiciones de carrera más que múltiples declaraciones, e introduce otros peligros:
http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Incluso con esta sintaxis "más simple" disponible, sigo prefiriendo este enfoque (el manejo de errores se omite por brevedad):
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;
Mucha gente sugerirá de esta manera:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
UPDATE ...
END
ELSE
INSERT ...
END
COMMIT TRANSACTION;
Pero todo lo que se logra es garantizar que deba leer la tabla dos veces para ubicar las filas que se actualizarán. En la primera muestra, solo necesitará ubicar las filas una vez. (En ambos casos, si no se encuentran filas de la lectura inicial, se produce una inserción).
Otros sugerirán de esta manera:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
UPDATE ...
END CATCH
Sin embargo, esto es problemático si no es por otra razón que dejar que SQL Server capture excepciones que podría haber evitado en primer lugar es mucho más costoso, excepto en el raro escenario en el que casi cada inserción falla. Aquí lo pruebo: