Многие люди предложат вам использовать 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 перехватывать исключения, которые вы могли бы предотвратить в первую очередь, намного дороже, за исключением редкого сценария, когда происходит почти каждая вставка. Я докажу здесь столько же: