1. ホーム
  2. sql

[解決済み] 数百万行をIDで削除する最適な方法

2023-06-09 11:35:30

質問

PG データベースから約 200 万行を削除する必要があります。私は、削除する必要がある ID のリストを持っています。しかし、私がこれをしようとするどの方法も、何日もかかっています。

私はそれらをテーブルに入れ、100 のバッチでそれを行うことを試みました。4 日後、これはまだ実行中で、297268 行しか削除されていません。(ID テーブルから 100 の ID を選択し、そのリストの中のどこを削除し、選択した 100 を ID テーブルから削除しなければなりませんでした)。

私は試しました。

DELETE FROM tbl WHERE id IN (select * from ids)

これも時間がかかっていますね。完了するまで進行状況を見ることができないので、どのくらいかかるのか計りかねますが、2日経ってもクエリが実行されたままでした。

削除する特定の ID がわかっていて、ID が何百万もある場合に、テーブルから削除する最も効果的な方法を探しているところなんです。

どのように解決するのですか?

それはすべて...

  • 仮定すると 同時書き込みアクセスなし を使用しないか、テーブルを排他的にロックしなければならないか、あるいはこのルートは全く向いていないかもしれません。

  • すべてのインデックスを削除します(おそらく削除自体に必要なものを除く)。

    その後、それらを再作成します。これは通常、インデックスへの増分更新よりもはるかに高速です。

  • 一時的に安全に削除/無効化できるトリガーがあるかどうかを確認します。

  • 外部キーはあなたのテーブルを参照していますか?それらは削除可能ですか?一時的に削除されますか?

  • autovacuum の設定によって、それは を実行するのに役立ちます。 VACUUM ANALYZE を実行するのに役立つかもしれません。

  • マニュアルの関連章に記載されているポイントの一部 データベースへの入力 も、設定次第では役に立つかもしれません。

  • テーブルの大部分を削除し、残りが RAM に収まる場合、最も高速で簡単な方法はこれかもしれません。

BEGIN; -- typically faster and safer wrapped in a single transaction

SET LOCAL temp_buffers = '1000MB'; -- enough to hold the temp table

CREATE TEMP TABLE tmp AS
SELECT t.*
FROM   tbl t
LEFT   JOIN del_list d USING (id)
WHERE  d.id IS NULL;      -- copy surviving rows into temporary table
-- ORDER BY ?             -- optionally order favorably while being at it

TRUNCATE tbl;             -- empty table - truncate is very fast for big tables

INSERT INTO tbl
TABLE tmp;        -- insert back surviving rows.

COMMIT;

この方法では、ビュー、外部キー、その他の依存するオブジェクトを再作成する必要がありません。そして、肥大化することなく、原始的な(ソートされた)テーブルを手に入れることができます。

について読む temp_buffers の設定については、マニュアル . この方法は、テーブルがメモリに収まる限り、あるいは少なくともそのほとんどが収まる限り、高速に動作します。トランザクションラッパーは、この操作の途中でサーバーがクラッシュした場合にデータを失わないようにするものです。

実行 VACUUM ANALYZE を後に実行します。または(通常 ではない を行った後に必要な TRUNCATE ルート) VACUUM FULL ANALYZE で最小サイズになります(排他ロックが必要です)。大きなテーブルの場合は、代替案として CLUSTER / pg_repack などとする。

小さなテーブルの場合、単純な DELETE の代わりに TRUNCATE の方が速い場合が多いです。

DELETE FROM tbl t
USING  del_list d
WHERE  t.id = d.id;

読む その ノート セクションで TRUNCATE マニュアルにある . 特に、( Pedro もコメントで指摘しているように ):

TRUNCATE は、他のテーブルからの外部キー参照を持つテーブルでは使用できません。 他のテーブルからの外部キー参照を持つテーブルでは、そのようなテーブルもすべて同じコマンドで切り捨てられない限り、使用できません。 ただし、そのようなテーブルがすべて同じコマンドで切り捨てられる場合はこの限りではありません。[...]

とか。

<ブロッククオート

TRUNCATE は、いかなる ON DELETE トリガーを起動しない。 を起動しません。