web-dev-qa-db-ja.com

T-SQLの挿入または更新

SQL Serverのパフォーマンスについて質問があります。

次の列を持つテーブルpersonsがあるとします:idnamesurname

次に、このテーブルに新しい行を挿入します。ルールは次のとおりです。

  1. idがテーブルに存在しない場合は、行を挿入します。

  2. idが存在する場合は、更新します。

私はここに2つの解決策を持っています:

最初:

update persons
  set id=@p_id, name=@p_name, surname=@p_surname
where id=@p_id
if @@ROWCOUNT = 0 
  insert into persons(id, name, surname)
  values (@p_id, @p_name, @p_surname)

第二:

if exists (select id from persons where id = @p_id)
  update persons
    set id=@p_id, name=@p_name, surname=@p_surname
  where id=@p_id
else
  insert into persons(id, name, surname)
  values (@p_id, @p_name, @p_surname)

より良いアプローチとは何ですか? 2番目の選択肢では、行を更新するには2回検索する必要があるようですが、最初のオプションでは1回だけです。問題の他の解決策はありますか? MS SQL 2000を使用しています。

36
Markus

どちらも問題なく動作しますが、私はオプション2(mssql 2008より前)を使用しています。ここでのパフォーマンスについても強調しません...問題になる場合は、NOLOCK句でexistsを使用できます。すべての場所でNOLOCKの使用を開始する前に、すべてのベース(インデックスや全体像のアーキテクチャなど)をカバーしていることを確認してください。すべてのアイテムを2回以上更新することがわかっている場合は、オプション1を検討することをお勧めします。

オプション3は、破壊的な更新を使用しないことです。作業量は増えますが、基本的には、データが変更されるたびに新しい行を挿入し(テーブルから更新または削除しないでください)、最新のすべての行を選択するビューを用意します。テーブルに以前のすべての状態の履歴を含める場合に便利ですが、やり過ぎになることもあります。

11
dan

オプション1は良いようです。ただし、SQL Server 2008を使用している場合は、 [〜#〜] merge [〜#〜] を使用することもできます。これは、このようなUPSERTタスクに適しています。

このようなタスクでは、明示的なトランザクションと XACT_ABORT オプションを使用して、問題または同時変更が発生した場合でもトランザクションの一貫性が維持されるようにしてください。

12
Lucero

オプション1を使用する傾向があります。テーブルにレコードがある場合、1つの検索を保存します。ない場合は、何も失うことはありません。さらに、2番目のオプションでは、ロックの非互換性に関連するおかしなロックおよびデッドロックの問題が発生する可能性があります。私のブログにはいくつかの情報があります:

http://sqlblogcasts.com/blogs/piotr_rodak/archive/2010/01/04/updlock-holdlock-and-deadlocks.aspx

4
Piotr Rodak

もう少しDRYになることを目指して、値のリストを2回書き出すことは避けます。

begin tran
insert into persons (id)
select @p_id from persons
 where not exists (select * from persons where id = @p_id)

update persons
set name=@p_name, surname=@p_surname
where id = @p_id

commit

nameおよびsurnameはNULL可能でなければなりません。

このトランザクションは、他のユーザーが「空の」レコードを見ることはないことを意味します。

編集:クリーンアップ

3
Patrick

@@ RowCountを使用して、更新によって何が行われたかを確認できます。何かのようなもの:

    UPDATE MyTable
       SET SomeData = 'Some Data' WHERE ID = 1
    IF @@ROWCOUNT = 0
      BEGIN
        INSERT MyTable
        SELECT 1, 'Some Data'       
      END
3
KISS