Sehr kurze Version: Ja, manchmal.
PostgreSQL kann Bitmap-Index-Scans verwenden, um mehrere Indizes zu kombinieren.
Ein Prädikat wie
WHERE a > 50 AND a < 50000
ist eine Spezialisierung der allgemeineren Form:
wHERE a > 50 and b < 50000
für a = b.
PostgreSQL kann hier zwei Indizes verwenden, einen für jeden Teil des Prädikats, und diese dann einer Bitmap AND
zuordnen. Es spielt keine Rolle, ob sie sich in verschiedenen Bereichen derselben Spalte befinden.
Dies ist viel weniger effizient als ein einzelner Index und für einige Abfragen möglicherweise nicht hilfreich, aber möglich.
Das größere Problem ist, dass die teilweise Indexunterstützung von PostgreSQL nicht sehr gut ist. Unabhängig davon, ob es einen oder zwei Indizes gibt, kann es sein, dass der Index überhaupt nicht verwendet werden kann.
Demonstrationsaufbau:
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, was wird Pg für gegebene Anfragen bevorzugen?
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)
Was ist, wenn wir versuchen, einen Bitmap-Index-Scan zu erzwingen, um herauszufinden, ob Pg einen verwenden kann , auch wenn es sich für diesen speziellen einfachen Fall und eine kleine Stichprobe nicht lohnt, dies zu tun?
Versuchen:
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)
Hm. Nee. Die Indizes dort nicht kombinieren. Es könnte aber einfach nicht glauben, dass es sich lohnt, einen zweiten Index zu scannen.
Was ist mit einer Abfrage, die stattdessen zwei Prädikate ODER-verknüpft?
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)
Hier hat PostgreSQL beide Indizes ODER-verknüpft, um eine Übereinstimmung zu finden, dann einen Heap-Scan durchgeführt und erneut überprüft.
Ja, PostgreSQL kann zumindest für einige Abfragen mehrere Teilindizes kombinieren, sofern dies sinnvoll ist.
Aber wenn ich RESET
den Planer überschreibe ...
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 wird erkennen, dass es schneller ist, die Tabelle nur nacheinander zu scannen.