Багато людей запропонують вам скористатися MERGE
, але я застерігаю від цього. За замовчуванням він не захищає вас від одночасності та перегонових умов, ніж декілька заяв, і вводить інші небезпеки:
http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Навіть із наявним "простішим" синтаксисом я все одно віддаю перевагу такому підходу (обробка помилок, опущена для стислості):
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;
Багато людей запропонують такий спосіб:
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;
Але все це домагається того, що вам може знадобитися прочитати таблицю двічі, щоб знайти рядки, які потрібно оновити. У першому зразку вам буде потрібно лише один раз знаходити рядки. (В обох випадках, якщо з початкового зчитування не знайдено рядків, виникає вставка.)
Інші запропонують такий спосіб:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
UPDATE ...
END CATCH
Однак це проблематично, якщо з будь-якої іншої причини, крім того, щоб дозволити вилученню SQL Server винятки, які ви могли б запобігти, в першу чергу, набагато дорожче, за винятком рідкісного сценарію, коли майже кожна вставка виходить з ладу. Я тут багато чого доводжу: