PostgreSQLは一般に複数のインデックスを使用できることを読みましたが、2つのインデックスにまたがるクエリの特定のケースでは、両方を使用しますか?もしそうなら、それらは順次または一緒にロードされますか?
たとえば、このクエリがcolumn_1
によって2つの部分インデックスにまたがる場合、部分インデックスが使用される場合、どのように使用され、インデックスデータはどのようにロードおよび破棄されますか。
SELECT 1 FROM sample_table WHERE column_1 > 50 AND column_2 < 50000
非常に短いバージョン:はい、時々。
PostgreSQLは、ビットマップインデックススキャンを使用して複数のインデックスを組み合わせることができます。
のような述語
WHERE a > 50 AND a < 50000
より一般的な形式の特殊化です:
wHERE a > 50 and b < 50000
a = bの場合。
PostgreSQLはここで、述語の各部分に1つずつ、次にビットマップAND
の2つのインデックスを使用できます。同じ列の異なる範囲にあるかどうかは関係ありません。
これはmuchは単一のインデックスよりも効率が悪く、一部のクエリでは役に立たない可能性がありますが、可能です。
より大きな問題は、PostgreSQLの部分インデックスのサポートがあまり明るくないことです。インデックスが1つであるか2つであるかに関係なく、インデックスを使用できることをまったく理解できない場合があります。
デモのセットアップ:
CREATE TABLE partial (x integer, y integer);
CREATE INDEX xs_above_50 ON partial(x) WHERE (x > 50);
CREATE INDEX xs_below_50000 ON partial(x) WHERE (x < 5000);
INSERT INTO partial(x,y) SELECT a, a FROM generate_series(1,100000) a;
OK、Pgは与えられたクエリに対して何を好みますか?
regress=> EXPLAIN SELECT y FROM partial WHERE x > 50 AND x < 50000;
QUERY PLAN
----------------------------------------------------------------------------------
Index Scan using xs_above_50 on partial (cost=0.29..1788.47 rows=50309 width=4)
Index Cond: ((x > 50) AND (x < 50000))
(2 rows)
regress=> EXPLAIN SELECT y FROM partial WHERE x > 20 AND x < 50000;
QUERY PLAN
--------------------------------------------------------------
Seq Scan on partial (cost=0.00..1943.00 rows=50339 width=4)
Filter: ((x > 20) AND (x < 50000))
(2 rows)
regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 AND x < 50000;
QUERY PLAN
----------------------------------------------------------------------------------
Index Scan using xs_above_50 on partial (cost=0.29..1787.45 rows=50258 width=4)
Index Cond: ((x > 100) AND (x < 50000))
(2 rows)
regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 AND x < 20000;
QUERY PLAN
---------------------------------------------------------------------------------
Index Scan using xs_above_50 on partial (cost=0.29..710.71 rows=19921 width=4)
Index Cond: ((x > 100) AND (x < 20000))
(2 rows)
この特定の単純なケースと小さなサンプルで行う価値がない場合でも、Pg canを使用するかどうかを確認するためだけにビットマップインデックススキャンを強制しようとするとどうなりますか?
試してください:
regress=> SET enable_seqscan = off;
SET
regress=> SET enable_indexscan = off;
SET
regress=> SET enable_indexonlyscan = off;
SET
regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 AND x < 20000;
QUERY PLAN
--------------------------------------------------------------------------------
Bitmap Heap Scan on partial (cost=424.48..1166.30 rows=19921 width=4)
Recheck Cond: ((x > 100) AND (x < 20000))
-> Bitmap Index Scan on xs_above_50 (cost=0.00..419.50 rows=19921 width=0)
Index Cond: ((x > 100) AND (x < 20000))
(4 rows)
うーん。いいえ。そこではインデックスを組み合わせません。ただし、2つ目のインデックスをスキャンする価値はあるかもしれませんが、そうは思わないかもしれません。
代わりに2つの述語をORするクエリについてはどうですか?
regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 OR x < 200;
QUERY PLAN
---------------------------------------------------------------------------------------
Bitmap Heap Scan on partial (cost=1905.29..3848.29 rows=99908 width=4)
Recheck Cond: ((x > 100) OR (x < 200))
-> BitmapOr (cost=1905.29..1905.29 rows=100000 width=0)
-> Bitmap Index Scan on xs_above_50 (cost=0.00..1849.60 rows=99908 width=0)
Index Cond: (x > 100)
-> Bitmap Index Scan on xs_below_50000 (cost=0.00..5.73 rows=193 width=0)
Index Cond: (x < 200)
(7 rows)
ここで、PostgreSQLは両方のインデックスのORを実行して一致を見つけ、ヒープスキャンを実行して再チェックしました。
そのため、はい、PostgreSQLは、少なくとも一部のクエリで複数の部分インデックスを組み合わせることができます。
しかし、私がRESET
の場合、プランナーはオーバーライドします...
regress=> RESET enable_seqscan;
RESET
regress=> RESET enable_indexscan ;
RESET
regress=> RESET enable_indexonlyscan ;
RESET
regress=> EXPLAIN SELECT y FROM partial WHERE x > 100 OR x < 200;
QUERY PLAN
--------------------------------------------------------------
Seq Scan on partial (cost=0.00..1943.00 rows=99908 width=4)
Filter: ((x > 100) OR (x < 200))
(2 rows)
... Pgは、テーブルを順次スキャンするだけの方が高速であることを認識します。