web-dev-qa-db-ja.com

postgresから行を一括削除する最も効率的な方法

PostgreSQLから大量の行を削除する最も効率的な方法は何だろうと思います。このプロセスは、データ(挿入と削除の差分)をテーブルに一括インポートするための毎日繰り返されるタスクの一部になります。削除する行は数千、場合によっては数百万になる可能性があります。

行ごとに1つの主キーのファイルがあります。私が考えていた2つのオプションは以下のとおりでしたが、PostgreSQLの内部を十分に理解していないため、情報に基づいた最適な決定を行うことができません。

  • ファイルの各行に対してDELETEクエリを実行します。主キーに単純なWHEREを使用します(またはIN()を使用してnのバッチに削除をグループ化します)句)
  • COPYコマンドを使用して主キーを一時テーブルにインポートし、結合を使用してメインテーブルから削除する

どんな提案も大歓迎です!

25
tarnfeld

2番目のオプションははるかにすっきりしており、その価値を十分に発揮します。代わりの方法は、巨大なクエリを作成することです。これは、計画と実行が非常に困難になります。一般的には、ここでPostgreSQLに作業を任せるほうがよいでしょう。一般に、適切に実行するために説明した方法で数万行の更新を検出しましたが、実行しないようにする重要なことが1つあります。

これを行う方法は、削除で選択と結合を使用することです。

DELETE FROM foo WHERE id IN (select id from rows_to_delete);

大きなテーブルでは、次のようにしてはいけません。

DELETE FROM foo WHERE id NOT IN (select id from rows_to_keep);

これにより、通常、ネストされたループのアンチジョインが発生し、パフォーマンスがかなり問題になります。そのルートに行かなければならない場合は、代わりにこれを行ってください:

DELETE FROM foo 
WHERE id IN (select id from foo f 
          LEFT JOIN rows_to_keep d on f.id = d.id
              WHERE d.id IS NULL);

PostgreSQLは通常、悪い計画を回避するのに非常に優れていますが、良い計画と悪い計画の間に大きな違いをもたらす可能性のある外部結合を伴うケースがまだあります。

これはもう少し遠くまでさまよっていますが、INからNOT INに移動してクエリパフォーマンスタンクを監視するのが非常に簡単であるため、言及する価値があると思います。

26
Chris Travers

私は同様の問題を抱えていたので、この質問に出くわしました。 300M以上の行があるデータベースをクリーンアップしています。最終的なデータベースには、元のデータの約30%しかありません。同様のシナリオに直面している場合は、実際に削除するのではなく、新しいテーブルに挿入してインデックスを再作成する方が簡単です。

のようなことをする

CREATE temp_foo as SELECT * FROM foo WHERE 1=2;
INSERT INTO temp_foo (SELECT * FROM foo where foo.id IN (SELECT bar.id FROM BAR);

Fooとbarの適切なインデックス付けにより、Seqスキャンを回避できます。

次に、テーブルのインデックスを再作成して名前を変更する必要があります。

2
Niro