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