Viele Leute werden vorschlagen, dass Sie verwenden MERGE
, aber ich warne Sie davor. Standardmäßig schützt es Sie nicht mehr als mehrere Anweisungen vor Parallelität und Race-Bedingungen und bringt andere Gefahren mit sich:
http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Selbst mit dieser "einfacheren" Syntax bevorzuge ich diesen Ansatz (Fehlerbehandlung der Kürze halber weggelassen):
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;
Viele Leute werden diesen Weg vorschlagen:
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;
Damit können Sie jedoch sicherstellen, dass Sie die Tabelle möglicherweise zweimal lesen müssen, um die zu aktualisierenden Zeilen zu finden. Im ersten Beispiel müssen Sie die Zeile (n) immer nur einmal suchen. (In beiden Fällen erfolgt eine Einfügung, wenn beim ersten Lesen keine Zeilen gefunden wurden.)
Andere schlagen diesen Weg vor:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
UPDATE ...
END CATCH
Dies ist jedoch problematisch, wenn es aus keinem anderen Grund viel teurer ist, SQL Server Ausnahmen abfangen zu lassen, die Sie an erster Stelle hätten verhindern können, außer in dem seltenen Szenario, in dem fast jede Einfügung fehlschlägt. Das beweise ich hier: