テーブルへの大きな変更には何が良いですか:毎回DELETEとINSERTまたは既存のUPDATEですか?


27

私は毎日1つのテーブルで約36Kレコードを変更する必要があるプロジェクトを作成しています。私は何が良くなるのだろうかと思っています:

  1. 行を削除して新しい行を挿入する、または
  2. 既存の行を更新する

私にとっては、すべての行を削除して新しい行を挿入する方が簡単ですが、これがテーブルとインデックスを断片化し、パフォーマンスに影響を与える場合、可能な限り更新を行い、必要な場合にのみ削除/挿入することをお勧めします。

これは毎晩のサービスになりますが、プロセス自体の速度を改善するつもりはありません。私は、このテーブルに対するクエリのパフォーマンスについて、私が既に8,900万件のレコードを持っている場合と、この夜間のプロセスがどのように影響するかについてより懸念しています。

この夜間のプロセスのために、レコードを削除/挿入するか、既存のレコードを更新する必要がありますか(可能な場合)?


フィールドのインデックスが存在する可能性に依存すると推測されるため、テーブルの詳細を追加する必要があると思います。
SRKX

回答:


9

それは、どのくらいのデータが変化しているかに本当に依存します。このテーブルに20列あるとしましょう。また、5つのインデックスがあり、それぞれが差分にあります。カラム。

20列すべての値が変化する場合、または5列のデータが変化し、これらの5列すべてにインデックスが付けられている場合でも、「削除と挿入」の方が良いかもしれません。ただし、2つの列のみが変更されており、これらが非クラスター化インデックスの一部ではない場合、この場合はクラスター化インデックスのみが更新されるため、レコードを「更新」した方がよい場合があります更新します)。


さらなる調査で、SQL ServerにはUPDATEを実行するための2つの独立したメカニズムがあるため、上記のコメントは冗長であることがわかりました。-「インプレース更新」(つまり、列の値を元の行の新しい値に変更する)、または「非インプレース更新」(DELETEの後にINSERT)。

インプレース更新がルールであり、可能であれば実行されます。ここで、行は同じエクステントの同じページの同じ場所に正確に留まります。影響を受けるバイトのみが変更されます。tlogには1つのレコードしかありません(更新トリガーがない場合)。ヒープが更新されている場合(およびページに十分なスペースがある場合)、更新が行われます。クラスタリングキーが変更されたが、行をまったく移動する必要がない場合にも、更新が行われます。

例:姓にクラスター化インデックスがあり、名前がAble、Baker、Charlieの場合、BakerをBeckerに更新します。行を移動する必要はありません。したがって、これはインプレースで実行できます。一方、AbleをKumarに更新する必要がある場合、行をシフトする必要があります(同じページにある場合でも)。この場合、SQL ServerはDELETEに続いてINSERTを実行します。

上記を考慮して、通常のUPDATEを実行し、SQL Serverが内部でそれを実行する最善の方法を把握できるようにすることをお勧めします。

"UPDATE"内部の詳細、またはSQL Server関連の内部については、Kalen Delaney、Paul Randalの著書、 SQL Server 2008 Internalsを参照してください


8

SQL 2008でMERGEコマンドを調査しましたか?基本的な例を次に示します。

  merge YourBigTable ybt
  using (select distinct (RecordID) from YourOtherTable) yot
     on yot.Recordid = YBT.RecordID
  when NOT matched by target
  then  insert (RecordID)
        values (yot.DeviceID) ;

これは基本的に「UPSERT」コマンドです。存在する場合は更新し、存在しない場合は挿入します。非常に高速で非常にクールなコマンド。


1
これは、UPDATEよりも高速ではありません。同じ仕組みです。
マークストーリースミス

更新してから、まだ存在していないものを挿入するよりも高速です。
datagod

2
そうだとわかっている場合は、それを証明してください:)
マークストーリースミス

4

しかし、私自身は、3000万(3crore)のレコードがあるテーブルでDeleteとInsert vs Updateをチェックしました。このテーブルには、1つのクラスター化された一意の複合キーと3つの非クラスター化キーがあります。削除と挿入の場合、9分かかりました。更新には55分かかりました。各行で更新された列は1つだけです。

だから、私はあなたの人々が推測しないように要求します。多くの列と多くのデータを持つ大きなテーブルを扱う場合、方程式は変わります。


また、このケースに遭遇しましたが、ソース(ターゲット)にインデックス(tempまたはperm)を追加することで、大規模なマージを最適化できる場合があることを発見しました。ヒント、またはターゲットのサブセット(完全マージには適用されません)。
crokusek

3

更新はそれほど速くありません。秘insertは、データを挿入している間はインデックスを無効にすることです。

これの使用を検討してください:

-- disable indexes
ALTER INDEX [index_name] ON dbo.import_table DISABLE
-- ... disable more indexes

-- don't use delete if you don't care about minimal logging. truncate is faster
TRUNCATE TABLE dbo.import_table

-- just insert the new rows
INSERT dbo.import_table
SELECT
    *
FROM
    dbo.source_table

-- rebuild indexes
ALTER INDEX [index_name] ON dbo.import_table REBUILD
-- ... rebuild more indexes

さらに速いのは、dbオプションで統計の自動更新をオフにすることです。テーブルが大幅に変更された場合は、次を実行する必要があります。

UPDATE STATISTICS dbo.import_table

または

EXEC sp_updatestats

定期的に(毎日、データベースサイズに応じて毎週)ジョブとして、統計を最新の状態に保ちます。注意すべきことは、テーブルが空のときに統計を更新することです。テーブルに再びデータが入力された後に実行しないと、統計が台無しになります。


4
これが常に当てはまることには同意しません。また、@ adopilotの質問の表は、89mのレコードが含まれており、36kのみを更新するため、TRUNCATEでクリアできません。
マークストーリースミス

投稿をより注意深く読むことを学ぶ必要があります!投稿を更新します...実際、私は多くを変更する必要があります。
アスケン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.