次のフィールドを持つ単純なテーブルがあるとします。
アプリケーションは常にNameフィールドの操作に基づいているため、ルックアップにIDフィールドを使用することはありません。
Tagの値を時々変更する必要があります。私は次のささいなSQLコードを使用しています:
UPDATE Table SET Tag = XX WHERE Name = YY;
上記が常により速いかどうかを誰かが知っているのだろうか?
DELETE FROM Table WHERE Name = YY;
INSERT INTO Table (Name, Tag) VALUES (YY, XX);
繰り返しますが、2番目の例ではIDが変更されることは知っていますが、アプリケーションにとっては問題ではありません。
テーブルが大きい(列の数とサイズ)ほど、更新ではなく削除と挿入のコストが高くなります。 UNDOとREDOの価格を支払う必要があるためです。 DELETEはUPDATEよりも多くのUNDOスペースを消費し、REDOには必要な2倍のステートメントが含まれます。
その上、ビジネスの観点からは間違いです。そのテーブルの概念的な監査証跡を理解することがどれほど難しいかを考えてください。
古いテーブルからCTASを使用して新しいテーブルを作成する方が速く(SELECT句のプロジェクションに更新を適用する)、古いテーブルを削除して、新しいテーブル。副作用は、インデックスの作成、制約の管理、特権の更新ですが、検討する価値があります。
同じ行の1つのコマンドは、同じ行の2つのコマンドよりも常に高速である必要があります。したがって、UPDATEの方が優れています。
[〜#〜] edit [〜#〜]テーブルをセットアップします。
create table YourTable
(YourName varchar(50) primary key
,Tag int
)
insert into YourTable values ('first value',1)
これを実行します。私のシステムでは1秒かかります(SQL Server 2005):
SET NOCOUNT ON
declare @x int
declare @y int
select @x=0,@y=0
UPDATE YourTable set YourName='new name'
while @x<10000
begin
Set @x=@x+1
update YourTable set YourName='new name' where YourName='new name'
SET @y=@y+@@ROWCOUNT
end
print @y
私のシステムでは2秒かかったこれを実行します:
SET NOCOUNT ON
declare @x int
declare @y int
select @x=0,@y=0
while @x<10000
begin
Set @x=@x+1
DELETE YourTable WHERE YourName='new name'
insert into YourTable values ('new name',1)
SET @y=@y+@@ROWCOUNT
end
print @y
あなたの質問の本文は、タイトルの質問とは無関係です。
タイトルに答える場合:
SQLでは、UPDATEはDELETE + INSERTよりも常に高速ですか?
答えはノーです!
ただグーグル
このような更新は、直接挿入+更新よりも挿入+更新による更新の実現にコストがかかります(処理が多くなります)。これらは次の場合です
私の高速(網羅的ではない)検索は、1つをカバーするふりをしていないため、[1]、[2]
[1]
更新操作
(Sybase®SQL Serverパフォーマンスおよびチューニングガイド
第7章:SQL Serverクエリオプティマイザー)
http://www.lcard.ru/~nail/sybase/perf/11500.htm
[2]
UPDATEステートメントはDELETE/INSERTペアとして複製される場合があります
http://support.Microsoft.com/kb/238254
44個のフィールドを持つテーブルの43個のフィールドを更新しようとしましたが、残りのフィールドはプライマリクラスター化キーでした。
更新には8秒かかりました。
削除+挿入は、「クライアント統計」がSQL Management Studioを介して報告する最小時間間隔よりも高速です。
ピーター
MS SQL 2008
正しく実装されたUPDATEではなく、DELETE + INSERTが発行されたときに発生する実際の断片化は、時間によって大きな違いをもたらすことに留意してください。
そのため、たとえば、MySQLが実装するREPLACE INTOは、INSERT INTO ... ON DUPLICATE KEY UPDATE ...構文を使用するのではなく、推奨されません。
数百万行がある場合はどうでしょう。各行は、おそらくクライアント名などの1つのデータで始まります。クライアントのデータを収集すると、そのエントリを更新する必要があります。ここで、クライアントデータのコレクションが他の多数のマシンに分散され、後で収集されてデータベースに入れられると仮定します。各クライアントに固有の情報がある場合、一括更新を実行できません。つまり、複数のクライアントを一度に更新するために使用するためのwhere-clause基準はありません。一方、一括挿入を実行できます。そのため、次のように質問する方が良いかもしれません:何百万もの単一の更新を実行する方が良いのか、それとも大規模な削除と挿入にコンパイルする方が良いのか。言い換えると、「[table] set field = data where clientid = 123」を数百回更新する代わりに、 'delete from [table] where clientid in([すべてのクライアントを更新する]); insert into [テーブル]値(client1のデータ)、(client2のデータ)など
どちらか一方が他方よりも優れていますか、それとも両方の方法でねじ込まれていますか?
あなたの場合、私は更新がより速くなると信じています。
インデックスを覚えてください!
主キーを定義すると、自動的にクラスター化インデックスになります(少なくともSQL Serverはそうします)。クラスターインデックスとは、インデックスに従ってレコードがディスク上に物理的に配置されることを意味します。 DELETE操作自体は、1つのレコードがなくなった後でも、インデックスが正しいままである場合でも、それほど問題を引き起こすことはありません。ただし、新しいレコードを挿入する場合、DBエンジンはこのレコードを正しい場所に配置する必要があります。状況によっては、古いレコードを「再配置」して新しいレコードを「配置」します。そこに操作が遅くなります。
インデックス(特にクラスター化された)は、値が増加し続ける場合に最適に機能するため、新しいレコードはテールに追加されます。多分、追加のINT IDENTITY列を追加してクラスター化インデックスにすることができます。これにより、挿入操作が簡単になります。
更新にはより多くの手順が含まれるため、削除+挿入はほとんど常に高速です。
更新:
ディスクへの新しい変数の書き込み(行全体)
(これは、更新する行ごとに繰り返されます)
削除+挿入:
新しいレコードの場所でPKインデックスを更新します。
(これは繰り返されません。すべてを単一の操作ブロックで実行できます)。
Insert + Deleteを使用すると、ファイルシステムが断片化されますが、それほど高速ではありません。バックグラウンドで遅延最適化を行うと、常に未使用のブロックが解放され、テーブルが完全にパックされます。
明らかに、答えは使用しているデータベースによって異なりますが、UPDATEはDELETE + INSERTよりも常に高速に実装できます。いずれにせよ、メモリ内の操作はほとんどの場合些細なことなので、ハードドライブベースのデータベースでは、UPDATEはhddのデータベースフィールドをその場で変更できますが、削除は行を削除し(空のスペースを残して)、新しいフィールドを挿入します行、おそらくテーブルの最後まで(これもすべて実装内にあります)。
もう1つのマイナーな問題は、1つの行の1つの変数を更新しても、その行の他の列は同じままであるということです。 DELETEしてからINSERTを行うと、他の列を忘れてしまい、結果として列を残してしまうリスクがあります(この場合、DELETEの前にSELECTを実行して、他の列をINSERTで書き戻す前に一時的に保存する必要があります) 。
速度の問題は、特定の速度の問題がなくても関係ありません。
既存の行に変更を加えるためのSQLコードを作成している場合は、それを更新します。それ以外は間違っています。
コードがどのように動作するかのルールを破ろうとするなら、それについての非常に良い、定量化された理由があり、「この方法は速い」という漠然とした考えはないほうがよい「高速」とは何かを考えます。
データベースへのすべての書き込みには、多くの潜在的な副作用があります。
削除:行の削除、インデックスの更新、外部キーのチェック、カスケード削除などを行う必要があります。挿入:行を割り当てる必要があります-これは削除された行の代わりにある場合があります。インデックスの更新、外部キーのチェックなどが必要です。更新:1つ以上の値を更新する必要があります。おそらく、行のデータはデータベースのそのブロックに収まらないので、より多くのスペースを割り当てる必要があり、それが書き換えられている複数のブロックにカスケードしたり、断片化されたブロックにつながる可能性があります。値に外部キー制約がある場合、チェックする必要があります。
列の数が非常に少ない場合、または行全体が更新される場合、Delete + insertの方が高速になる可能性がありますが、FK制約の問題は大きなものです。確かに、FKの制約は現在ないかもしれませんが、それは常に真実でしょうか?また、トリガーがある場合、更新操作が本当に更新である場合、更新を処理するコードを作成する方が簡単です。
考慮すべきもう1つの問題は、挿入と削除が更新とは異なるロックを保持する場合があることです。レコードの更新中に単一のレコードをロックするのではなく、挿入または削除中にDBがテーブル全体をロックする場合があります。
最後に、更新するつもりならレコードを更新することをお勧めします。次に、DBのパフォーマンス統計とそのテーブルの統計を確認して、パフォーマンスの改善が必要かどうかを確認します。それ以外は時期尚早です。
私が取り組んでいるeコマースシステムの例:データベースにクレジットカードトランザクションデータを2段階のアプローチで保存していました。最初に、プロセスを開始したことを示す部分的なトランザクションを記述します。次に、銀行から承認データが返されたら、レコードを更新します。レコードを削除してから再挿入できましたが、代わりに更新を使用しました。 DBAは、DBが各行に少量のスペースを割り当てているだけであり、大量のデータが追加されたためにブロックチェーンが発生したため、テーブルが断片化されたと言いました。ただし、DELETE + INSERTに切り替えるのではなく、常に行全体を割り当てるようにデータベースを調整しただけなので、更新では事前に割り当てられた空のスペースを問題なく使用できます。コードを変更する必要はなく、コードはシンプルで理解しやすいままです。
製品によって異なります。すべてのUPDATEを(トランザクションでラップされた)DELETEおよびINSERTに変換する(内部で)製品を実装できます。結果がUPDATEセマンティクスと一致している場合。
これを行う製品を知っているとは言いませんが、完全に合法です。
特定のケースでは、Delete + Insertで時間を節約できます。 30000の奇数行があるテーブルがあり、データファイルを使用してこれらのレコードの毎日の更新/挿入があります。アップロードプロセスでは、レコードが既に存在するため、更新ステートメントの95%と、存在しないレコードの挿入の5%が生成されます。または、データファイルレコードを一時テーブルにアップロードし、一時テーブル内のレコードの宛先テーブルを削除した後、一時テーブルから同じものを挿入すると、時間が50%増加します。