Wiele osób zasugeruje ci użycie MERGE
, ale ostrzegam cię przed tym. Domyślnie nie chroni cię przed współbieżnością i warunkami wyścigowymi więcej niż wiele oświadczeń, ale wprowadza inne niebezpieczeństwa:
http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Nawet przy tej dostępnej „prostszej” składni nadal wolę to podejście (pominięcie obsługi błędów ze względu na zwięzłość):
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;
Wielu ludzi zasugeruje w ten sposób:
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;
Ale to wszystko zapewnia, że konieczne może być dwukrotne przeczytanie tabeli w celu zlokalizowania wierszy, które mają zostać zaktualizowane. W pierwszej próbce będziesz musiał tylko zlokalizować rząd (wiersze) tylko raz. (W obu przypadkach, jeśli nie zostaną znalezione wiersze z początkowego odczytu, nastąpi wstawienie).
Inni sugerują w ten sposób:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
UPDATE ...
END CATCH
Jest to jednak problematyczne, jeśli tylko z innego powodu niż zezwolenie SQL Serverowi na wychwytywanie wyjątków, którym można było zapobiec, jest znacznie droższe, z wyjątkiem rzadkiego scenariusza, w którym prawie każda wstawka ulega awarii. Udowadniam tutaj tyle samo: