パフォーマンスのための何百万行にわたるSQL重複削除クエリ
-
03-07-2019 - |
質問
これは冒険でした。 私の前の質問にあるループ重複クエリから始めましたが、各ループはすべての 1700万レコード、数週間かかることを意味する(MSSQL 2005を使用すると、 * select count * from MyTable *
を実行するだけで4:30分かかります)。このサイトとこの投稿から情報を拝見しました。 。
そして、以下のクエリに到達しました。問題は、これがあらゆるタイプのパフォーマンスに対して1,700万件のレコードで実行する正しいタイプのクエリですか?そうでない場合、何ですか?
SQLクエリ:
DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in
(SELECT RecordID
FROM tl_acxiomimport.dbo.tblacxiomlistings
EXCEPT
SELECT RecordID
FROM (
SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
) al WHERE Rank = 1)
解決
QueryPlanを見ると役立ちます。
これは可能ですか?
SELECT m.*
into #temp
FROM tl_acxiomimport.dbo.tblacxiomlistings m
inner join (SELECT RecordID,
Rank() over (Partition BY BusinessName,
latitude,
longitude,
Phone
ORDER BY webaddress DESC,
caption1 DESC,
caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
) al on (al.RecordID = m.RecordID and al.Rank = 1)
truncate table tl_acxiomimport.dbo.tblacxiomlistings
insert into tl_acxiomimport.dbo.tblacxiomlistings
select * from #temp
他のヒント
DB、サーバー、ストレージ、またはそれらの組み合わせに問題があります。選択カウントの4:30 *は非常に高いようです。
DBCC_SHOWCONTIGを実行して、テーブルがどの程度断片化されているかを確認します。これにより、そのサイズのテーブルでパフォーマンスが大幅に低下する可能性があります。
また、RyanKeeterによるコメントに追加するには、ショープランを実行し、テーブルスキャンがある場合、そのテーブルのPKフィールドのインデックスを作成します。
もっと簡単なことではないでしょうか:
DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in
(SELECT RecordID
FROM (
SELECT RecordID,
Rank() over (Partition BY BusinessName,
latitude,
longitude,
Phone
ORDER BY webaddress DESC,
caption1 DESC,
caption2 DESC) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
)
WHERE Rank > 1
)
これをクエリアナライザーで実行します:
SET SHOWPLAN_TEXT ON
クエリアナライザーにクエリを実行するように依頼します。クエリを実行する代わりに、SQL Serverはクエリプランを生成して結果セットに入れます。
クエリプランを表示します。
1700万件のレコードは何もありません。 select count(*)を実行するのに4:30しかかからない場合、おそらくサーバーのメモリ不足または本当に古いプロセッサのいずれかに関連する深刻な問題があります。
パフォーマンスのために、マシンを修正します。 2GBまでポンプします。 RAMは最近非常に安く、そのコストはあなたの時間よりもはるかに少ないです。
クエリの実行中にプロセッサまたはディスクがスラッシングしていますか?そうでない場合、何かが呼び出しをブロックしています。その場合、クリーンアップの実行に要する時間だけデータベースをシングルユーザーモードにすることを検討してください。
では、最初にランク付けされていないすべてのレコードを削除していますか?結合を上位1つのサブクエリと比較する価値があるかもしれません(ランクは2005以上であるため、2000年でも機能する可能性があります)
1回の操作ですべての重複を削除する必要がありますか?何らかのハウスキーピングタスクを実行していると仮定すると、ピース単位で実行できる可能性があります。
基本的に、すべてのレコードをループする(ダーティリード)カーソルを作成し、それぞれの重複を削除します。全体的にかなり遅くなりますが、各操作は比較的最小限になります。その後、ハウスキーピングは毎晩のバッチではなく、一定のバックグラウンドタスクになります。
最初に一時テーブルを選択するという上記の提案が最善の策です。次のようなものを使用することもできます。
set rowcount 1000
削除を実行する前に。 1000行を削除すると実行を停止します。その後、0レコードが削除されるまで何度も実行します。
正しく取得した場合、クエリは次と同じです
DELETE tl_acxiomimport.dbo.tblacxiomlistings
FROM
tl_acxiomimport.dbo.tblacxiomlistings allRecords
LEFT JOIN (
SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
WHERE Rank = 1) myExceptions
ON allRecords.RecordID = myExceptions.RecordID
WHERE
myExceptions.RecordID IS NULL
より高速に実行する必要があると思います。「IN」を使用しないようにする傾向があります。可能な限りJOINを支持する条項。
たとえば、FROM部分で SELECT *
または SELECT COUNT(*)
を呼び出すだけで、実際に速度と結果を安全にテストできます。例:
SELECT *
FROM
tl_acxiomimport.dbo.tblacxiomlistings allRecords
LEFT JOIN (
SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
WHERE Rank = 1) myExceptions
ON allRecords.RecordID = myExceptions.RecordID
WHERE
myExceptions.RecordID IS NULL
それが、JOINアプローチを好むもう1つの理由です これがお役に立てば幸いです
これは正常に見えますが、データを一時テーブルに選択し、それを削除ステートメントで使用することを検討できます。 1つのクエリですべてを行うのではなく、これを行うことでパフォーマンスが大幅に向上することに気付きました。
大規模な削除を行う場合は、最初に適切なバックアップをとることをお勧めします(また、通常、削除されたレコードを別のテーブルにコピーします。念のため、すぐにリカバリする必要があります)。
提案されたように切り捨てを使用する以外に、このテンプレートを使用してテーブルから多くの行を削除するのは最高の幸運でした。覚えていないのですが、トランザクションを使用することで、ログファイルが大きくなるのを防ぐことができたと思います。そして、私は通常、次のようなことをする前にトランザクションロギングメソッドをシンプルに切り替えます。
SET ROWCOUNT 5000 WHILE 1 = 1 BEGIN begin tran DELETE FROM ??? WHERE ??? IF @@rowcount = 0 BEGIN COMMIT BREAK END COMMIT END SET ROWCOUNT 0