web-dev-qa-db-ja.com

存在する場合は更新、SQL Server 2008ではINSERT

1つのステートメントを使用してSQL ServerでUPSERTまたは言い換えるとUPDATE if records exists Else enter new record操作を使用する方法を知りたいですか。

この例は、Oracleでこれを実現する方法を示しています Here ただし、SQL Serverに存在しないDualテーブルを使用します。

だから、SQL Serverの代替案(ストアドプロシージャなし)してください?

39
Maven

多くの人が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
BEGIN
  INSERT ...
END
COMMIT TRANSACTION;

ただし、これにより、テーブルを2回読み取って更新する行を見つける必要がある場合があります。最初のサンプルでは、​​行を1回だけ見つける必要があります。 (どちらの場合も、最初の読み取りで行が見つからない場合、挿入が発生します。)

他の人はこの方法を提案します:

BEGIN TRY
  INSERT ...
END TRY
BEGIN CATCH
  IF ERROR_NUMBER() = 2627
    UPDATE ...
END CATCH

ただし、これは、ほとんどすべての挿入が失敗するまれなシナリオを除き、そもそも防止できた例外をSQL Serverにキャッチさせる以外の理由ではるかに高価な場合、問題があります。私もここで証明します:

単一のステートメントを持つことで得られると思うものがわからない。何も得られないと思います。 MERGEは単一のステートメントですが、実際には複数の操作を実行する必要があります。

91
Aaron Bertrand