2011-02-03 22 views
4

PostgreSQLのマルチカラムbtreeインデックスを利用して、2つのテーブル間で厄介な結合を実行しようとしています。PostgreSQLマルチカラムインデックスの比較( "<" and ">")演算子

   Table "revision_main" 
    Column  |   Type   | Modifiers 
----------------+------------------------+----------- 
revision_id | integer    | 
page_id  | integer    | 

Indexes: 
    "revision_main_pkey" UNIQUE, btree (revision_id) 
    "revision_main_cluster_idx" btree (page_id, "timestamp") CLUSTER 

この表には、wikiの改訂版(〜3億行)が含まれています。私のテーブルにはもっと多くの列がありますが、私はこの例のためにそれらを破棄しました。なぜなら、それらは重要ではないからです。

   Table "revert" 
     Column  | Type | Modifiers 
--------------------+---------+----------- 
page_id   | integer | 
revision_id  | integer | 
reverted_to  | integer | 
Indexes: 
    "revert_page_between_idx" btree (page_id, reverted_to, revision_id) CLUSTER 

この表には、復帰改訂版(約2200万行)が含まれています。リビジョンが元に戻った場合、revision_idはrevision_mainテーブルに行を持ち、revision_idとrevision_idの間にrevision_idがあり、同じpage_idを共有します。 (興味があればhttp://en.wikipedia.org/wiki/Wikipedia:Revertを参照してください)

復帰したリビジョンを取得するためにこれらの2つのテーブルを結合するのは簡単です。ここで私が作ってみたものです:元に戻すにクラスタ化インデックスが(したがって、「<」のような比較演算子をサポートし、「>」)Bツリーインデックスであるべきにもかかわらず

explain SELECT 
    r.revision_id, 
    rvt.revision_id 
FROM revision_main r 
INNER JOIN revert rvt 
    ON r.page_id = rvt.page_id 
    AND r.revision_id > rvt.reverted_to 
    AND r.revision_id < rvt.revision_id; 
             QUERY PLAN            
---------------------------------------------------------------------------------------------------- 
Merge Join (cost=4202878.87..15927491478.57 rows=88418194298 width=8) 
    Merge Cond: (r.page_id = rvt.page_id) 
    Join Filter: ((r.revision_id > rvt.reverted_to) AND (r.revision_id < rvt.revision_id)) 
    -> Index Scan using revision_main_page_id_idx on revision_main r (cost=0.00..9740790.61 rows=223163392 width=8) 
    -> Materialize (cost=4201592.06..4536465.21 rows=26789852 width=12) 
     -> Sort (cost=4201592.06..4268566.69 rows=26789852 width=12) 
       Sort Key: rvt.page_id 
       -> Seq Scan on revert rvt (cost=0.00..438534.52 rows=26789852 width=12) 

、クエリオプティマイザはしていません参加のために索引を使用し、「説明する」は、総費用が150億を超えると予測します(来年に行われる可能性があります)。

複数の列(btree)インデックスで比較演算子を使用することはできませんか?私は間違っているだけですか?

答えて

5

オプティマイザがあなたの仕事よりも優れているようです。

テーブルの一部を選択する場合(ハードウェアに依存する部分、つまり5%とします)、索引を使用する場合よりも表全体を選択して順序付けする方が高速です。いくつかの行を選択しただけの場合は、インデックスを使用する必要があります。そのため、データに対して正しいクエリプランが提供されます。

合計コストは、これらの数値がすべてBSであり、1つのクエリ内で互いに比較した場合にのみ有効です。 (2つの非常によく似たクエリによって生成される総コストは、非常に異なるスケールになる可能性があります)。実行時間とクエリコストはほとんど関係ありません。

+0

を私の経験では、コスト見積もりは実行時間を一貫して反映する傾向があります。その一方で、数字が何を意味するかはわからないので、私はあなたの理解には容認します。クエリを実行して番号を無視することをお勧めしますか? – halfak

+0

@halfak:もっと詳しく見てみましょう。データベースは小さなテーブルとの結合を開始するのが好きです。 revision_mainに(page_id、revision_id)のインデックスを追加すると、より効率的なクエリが得られる可能性があります。それはまた悪化するかもしれません。しかし、それが失敗した場合、それをより効率的にする唯一の方法は、より少ないデータを求める方法を見つけることです。 – btilly

0

復帰テーブル全体を読み込み、復帰テーブルの各行に適切なリビジョン行を見つける必要があるように、クエリは(SQLに基づいて)見えます。

復帰テーブル全体を読み取る必要があるため、その復帰テーブルの順次スキャンが適切です。おおよそ正しい数の行があると思われます。

各復帰行は、インデックススキャンとマージ結合によって最もよく行われると思われるいくつかのリビジョンに一致します。平均的に、各復帰行はおよそ3300のリビジョンと一致し、880億行になると推定しています。

880億行をすばやく選択する方法はわかりません。

さらに正確な見積もりを得るには、それぞれの復帰でカバーされるリビジョンが3300未満であることをPostgreSQLに確信させる方法が必要です。

リビジョンを復帰させた後、複数のリバートに含まれていても、各リビジョンは1回だけ表示されます。

だから、これはしかし、あなたに復帰リビジョンを与えることはありません代わりにINNER JOIN

の​​を使用してみてください:私はちょうどテーブル全体をソートするインデックスを使用するよりも速い可能性がどのように見ることができます

EXPLAIN 
SELECT 
    r.revision_id 
FROM revision_main r 
WHERE EXISTS (SELECT 1 FROM revert rvt 
    WHERE r.page_id = rvt.page_id 
    AND r.revision_id > rvt.reverted_to 
    AND r.revision_id < rvt.revision_id); 
+0

"各復帰行はおよそ3300のリビジョンと一致し、880億行になります。 --- 私は見る...実際には、それぞれの復帰は、復帰行の99%に対して1つの復帰と一致する必要があります。これを明白にするための方法はありますか? – halfak

+0

復帰が発生したときに、復帰したページのリビジョンを見つけて保存することができます。 –

関連する問題