85

PG データベースから約 200 万行を削除する必要があります。削除する必要がある ID のリストがあります。ただし、これを行うには何日もかかります。

それらをテーブルに入れて、100のバッチで実行しようとしました.4日後、これはまだ297268行が削除されたまま実行されています. (IDテーブルから100個のIDを選択し、そのリストのどこを削除し、選択した100個をIDテーブルから削除する必要がありました)。

私は試した:

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

それも永遠にかかります。完了するまで進行状況を確認できないため、どれくらいの時間を測定するのが難しいですが、クエリは 2 日後も実行されていました。

削除する特定のIDがわかっているときにテーブルから削除する最も効果的な方法を探しているだけで、何百万ものIDがあります。

4

8 に答える 8

118

それはすべて依存しています...

  • 関連するテーブルへの同時書き込みアクセスがない場合、またはテーブルを排他的にロックする必要がある場合、またはこのルートがまったく適していない場合があります。

  • すべてのインデックスを削除します (おそらく、削除自体に必要なものを除く)。
    後で再作成します。これは通常、インデックスの増分更新よりもはるかに高速です。

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

  • 外部キーはテーブルを参照していますか? 削除できますか? 一時的に削除?

  • 自動バキュームの設定によっては、操作の前に実行すると役立つ場合があります。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後で実行します。または(通常、ルートに移動した後は必要ありません)最小サイズにします(排他ロックを取ります)。大きなテーブルの場合は、代替手段を検討してください/または同様の:TRUNCATEVACUUM FULL ANALYZECLUSTERpg_repack

小さなテーブルの場合、多くの場合、単純なDELETE代わりのTRUNCATEほうが高速です。

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

マニュアル注記セクションをTRUNCATEお読みください。特に(ペドロも彼のコメントで指摘したように):

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

と:

TRUNCATEON DELETEテーブルに存在する可能性のあるトリガーは起動しません。

于 2011-11-28T02:42:46.380 に答える
5

私は自分でこの問題にぶつかりました。私にとって、最も速い方法は、WITHクエリをUSINGと組み合わせて使用​​ することでした

基本的に、WITHクエリは、削除したいテーブルで削除する主キーを持つ一時テーブルを作成します。

WITH to_delete AS (
   SELECT item_id FROM other_table WHERE condition_x = true
)
DELETE FROM table 
USING to_delete 
WHERE table.item_id = to_delete.item_id 
  AND NOT to_delete.item_id IS NULL;

もちろん、SELECTWITHクエリの内部は、複数の結合などを含む他の選択と同じくらい複雑になる可能性があります。削除する必要があるターゲットテーブルのアイテムを識別するために使用される1つ以上の列を返すだけです。

AND NOT to_delete.item_id IS NULLおそらく必要ありませんが、あえて試しませんでした。

その他の考慮事項は次のとおりです。

  1. 外部キーを介してこのテーブルを参照する他のテーブルにインデックスを作成します。これにより、特定の状況で数時間かかる削除をわずか数秒に短縮できます
  2. 制約チェックの遅延: これによりどの程度の改善が達成されるかは明らかではありませんが、これによれば、パフォーマンスが向上する可能性があります。欠点は、外部キー違反がある場合、最後の瞬間にしかそれを学習しないことです。
  3. 危険ですが、大きな可能性があります:削除中の定数チェックとトリガーを無効にします
于 2020-06-28T20:52:38.550 に答える
4

PostgreSQL の更新/削除のパフォーマンスが Oracle ほど強力ではないことはわかっています。何百万、何千万もの行を削除する必要がある場合、それは非常に難しく、長い時間がかかります。

ただし、本番データベースではこれを行うことができます。以下は私の考えです:

最初に、2 つの列を持つログ テーブルを作成する必要がありidます。flagidflagYnullY

後で、関数を作成します。10,000 行ごとに削除タスクを実行します。詳細については、私のブログをご覧ください。中国語ですが、そこにある SQL コードから必要な情報を取得できます。

id実行が高速になるため、両方のテーブルの列がインデックスであることを確認してください。

于 2011-11-28T06:37:16.757 に答える
2

考えられる答えは 2 つあります。

  1. レコードを削除しようとすると、テーブルに多数の制約またはトリガーが関連付けられている場合があります。多くのプロセッサ サイクルと他のテーブルからのチェックが発生します。

  2. このステートメントをトランザクション内に配置する必要がある場合があります。

于 2011-11-28T02:40:51.970 に答える
2

最初に、削除元のテーブルと削除 ID に使用しているテーブルの両方で、ID フィールドにインデックスがあることを確認します。

一度に100は少なすぎるようです。1000 または 10000 を試してください。

削除 ID テーブルから何も削除する必要はありません。バッチ番号の新しい列を追加し、バッチ 1 の場合は 1000、バッチ 2 の場合は 1000 などを入力して、削除クエリにバッチ番号が含まれていることを確認します。

于 2011-11-28T02:42:10.063 に答える
2

削除する ID を除くテーブル内のすべてのデータを新しいテーブルにコピーし、名前を変更してテーブルを交換することもできます (それを行うのに十分なリソースがある場合)。

これは専門家のアドバイスではありません。

于 2011-11-28T02:34:45.490 に答える
1

これを行う最も簡単な方法は、すべての制約を削除してから削除することです。

于 2011-11-28T02:34:15.937 に答える