Ответ 1
Многие люди будут предлагать вам использовать MERGE
, но я предостерегаю вас от этого. По умолчанию он не защищает вас от concurrency и условий гонки больше, чем несколько заявлений, но он вводит другие опасности:
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
BEGIN
INSERT ...
END
COMMIT TRANSACTION;
Но все это достигается тем, что вам может потребоваться прочитать таблицу дважды, чтобы найти строки, которые нужно обновить. В первом примере вам только нужно будет найти строку один раз. (В обоих случаях, если строки из начального чтения не найдены, происходит вставка.)
Другие будут предлагать этот способ:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
UPDATE ...
END CATCH
Однако это проблематично, если только для SQL Server исключение исключений, которое вы могли бы предотвратить в первую очередь, намного дороже, за исключением редкого сценария, где почти каждая вставка терпит неудачу. Я доказываю здесь:
- http://www.mssqltips.com/sqlservertip/2632/checking-for-potential-constraint-violations-before-entering-sql-server-try-and-catch-logic/
- http://www.sqlperformance.com/2012/08/t-sql-queries/error-handling
Не уверен, что вы думаете, что получаете от одного заявления; Я не думаю, что ты что-то получаешь. MERGE
- это один оператор, но он все равно должен выполнять несколько операций в любом случае, даже если это заставляет вас думать, что это не так.