2009-05-26 23 views
2

非常に具体的な質問を申し訳ありません。同じテーブルを参照する列を含む行を削除すると、時間がかかりません

私はテーブルを持っています(下を見てください)、私はそれから多くのレコードを削除しようとすると、私のPostgreSQL 8.2.5は親子制約を行う時間の98%を費やします。 私はそれを速くするためにどのインデックスを追加すべきかを理解しようとしています。 私は、このテーブルのすべての列がparent_block_idとして0またはnullのいずれかを持っていると言う必要があります。それは基本的なものです。

別のインデックスを追加しようとしました:just(parent_block_id);どこにparent_block_id = 0; parent_block_idはNULLです。 WHERE parent_block_id!= 0.これらのどちらも重大なパフォーマンス上の利益をもたらしませんでした。

varshavka=> explain analyze delete from infoblocks where template_id = 112; 
               QUERY PLAN 
------------------------------------------------------------------------------------------------------------- 
Seq Scan on infoblocks (cost=0.00..1234.29 rows=9 width=6) (actual time=13.271..40.888 rows=40000 loops=1) 
    Filter: (template_id = 112) 
Trigger for constraint $1: time=4051.219 calls=40000 
Trigger for constraint $2: time=1616.194 calls=40000 
Trigger for constraint cs_ibrs: time=2810.144 calls=40000 
Trigger for constraint cs_ibct: time=4026.305 calls=40000 
Trigger for constraint cs_ibbs: time=3517.640 calls=40000 
Trigger for constraint cs_ibreq: time=774344.010 calls=40000 
Total runtime: 790760.168 ms 
(9 rows) 



varshavka=> \d infoblocks 
             Table "public.infoblocks" 
    Column  |   Type    |      Modifiers 
-----------------+-----------------------------+------------------------------------------------------ 
id    | integer      | not null default nextval(('IB_SEQ'::text)::regclass) 
parent_block_id | integer      | 
nm_id   | integer      | default 0 
template_id  | integer      | not null 
author_id  | integer      | 
birthdate  | timestamp without time zone | not null 
Indexes: 
    "infoblocks_pkey" PRIMARY KEY, btree (id) 
    "zeroparent" btree (parent_block_id) WHERE parent_block_id <> 0 
Foreign-key constraints: 
    "$2" FOREIGN KEY (nm_id) REFERENCES newsmakers(nm_id) ON DELETE RESTRICT 
    "$5" FOREIGN KEY (author_id) REFERENCES users(user_id) ON DELETE RESTRICT 
    "cs_ibreq" FOREIGN KEY (parent_block_id) REFERENCES infoblocks(id) ON DELETE CASCADE 

答えて

2

まず、醜いクエリ時間に気づいたときに最初に行うべきことは、最近VACUUM ANALYZE dであることを確認してください。

一度限りの削除が必要な場合は、araqnid's answerをご覧ください。しかし、一部の行がゼロでない、ヌル以外のparent_block_idフィールドを持っているときに、今後も動作するものが必要な場合は、をお読みください。

私は、ON DELETE CASCADEによって引き起こされた削除を1つのクエリに結合しないと推測しています。EXPLAIN出力がこれらのトリガを示すという事実は、実際には子の行削除が実際に別々に行われることを示唆しています。おそらく各行はparent_block_idのインデックス付きルックアップを使用して検出されますが、それでもテーブルを1回掃引するよりもはるかに遅くなります。

ON DELETE CASCADEON DELETE RESTRICTに変更し、一時テーブルで実行する必要があるすべての削除のリストを手作業でコンパイルし、一度にすべて削除することで、おそらく大きなスピードアップを得ることができます。 階層の最大深度が小さい場合、この方法は非常に高速になります。はここにいくつかの擬似コードです:

# Insert the top-level rows as "seed" rows. 
INSERT INTO rows_to_delete 
    SELECT id, 0 FROM infoblocks WHERE template_id = 112 

# Gather all rows that are children of any row at depth curLevel, 
# advancing curLevel until no more children are found. 
curLevel = 0 
while (nRowsReturnedFromLastInsert > 0) { 
    INSERT INTO rows_to_delete 
     SELECT ib.id, rtd.level + 1 
     FROM infoblocks ib 
     JOIN rows_to_delete rtd ON (ib.parent_block_id = rtd.id) 
     WHERE rtd.level = curLevel 

    curLevel = curLevel + 1 
} 

DELETE FROM infoblocks 
    JOIN rows_to_delete rtd ON (infoblocks.id = rtd.id) 

(私はわからないんだけど、あなたが実際に成功するために、最終的なDELETEためON DELETE RESTRICTの代わりにON DELETE NO ACTIONを使用する必要があります - それは、単一のDELETE文があるかどうか私にははっきりしていません何らかの理由で受け入れられない場合は、最初に最下位レベルを削除し、次に最下位レベルを削除し、次にその次の最下位レベルなどを繰り返すことができます)

+0

ヒントありがとう、私は明日それを試してみます。 VACUUM ANALYZEに関して、私はそれをしました、それは〜10倍の絶対数で助けましたが、根底にあるO(N^2)をちょっと変えませんでした。 – alamar

+0

あなたは絶対に正しいですが、DELETE RESTRICTは魔法のように破壊されました! – alamar

2

template_idにインデックスを追加しようとしましたか?

+1

ON DELETE RESTRICTが有効な場合、親とその子孫すべてを削除することができます。これは本当の問題であり、逐次スキャンを行い、そのうちの1つのみです。 –

+0

identフィールドがソートされていないか、ルックアップ以外のものに使用されていない場合はハッシュインデックスを付けます。順序が意味がある場合はbtreeです。 –

+0

問題はseqスキャンではありません。問題は、制約cs_ibreqのトリガーです:time = ** 774344。010 ** calls = 40000 – alamar

2

しばらくの間他の人をブロックすることができる場合は、制約cs_ibreqを削除して、削除してから、制約を再追加してください。

おそらく、parent_block_idに1つの非NULL値があるため、制約をチェックするときにインデックスを使用していない可能性がありますか?それは少し奇妙に思えますが。

+0

+1しかし、いくつかのレコードに0以外の、nullでないparent_block_idフィールドがある場合、これは機能しません。 –

+0

+1このヒントは、Oracle DBでよく使用されます。 – ATorras

+0

このようなインデックスでは、結果はほぼ同じです。 – alamar

関連する問題