Tabelle t
hat zwei Indizes:
create table t (a int, b int);
create type int_pair as (a int, b int);
create index t_row_idx on t (((a,b)::int_pair));
create index t_a_b_idx on t (a,b);
insert into t (a,b)
select i, i
from generate_series(1, 100000) g(i)
;
Für den any
Operator wird kein Index verwendet :
explain analyze
select *
from t
where (a,b) = any(array[(1,1),(1,2)])
;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Seq Scan on t (cost=0.00..1693.00 rows=1000 width=8) (actual time=0.042..126.789 rows=1 loops=1)
Filter: (ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
Rows Removed by Filter: 99999
Planning time: 0.122 ms
Execution time: 126.836 ms
Aber einer von ihnen wird mit dem in
Operator verwendet:
explain analyze
select *
from t
where (a,b) in ((1,1),(1,2))
;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Index Only Scan using t_a_b_idx on t (cost=0.29..8.32 rows=1 width=8) (actual time=0.028..0.029 rows=1 loops=1)
Index Cond: (a = 1)
Filter: ((b = 1) OR (b = 2))
Heap Fetches: 1
Planning time: 0.161 ms
Execution time: 0.066 ms
Es verwendet den Datensatzindex, wenn der Datensatz in den richtigen Typ umgewandelt wird:
explain analyze
select *
from t
where (a,b)::int_pair = any(array[row(1,1),row(1,2)])
;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Index Scan using t_row_idx on t (cost=0.42..12.87 rows=2 width=8) (actual time=0.106..0.126 rows=1 loops=1)
Index Cond: (ROW(a, b)::int_pair = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
Planning time: 0.208 ms
Execution time: 0.203 ms
Warum verwendet der Planer den Nicht-Datensatz-Index für den any
Bediener nicht wie für den in
Bediener?
Antworten:
Intern gibt es zwei getrennte Formen von
IN
sowie für dasANY
Konstrukt.Einer von beiden, der eine Menge nimmt , entspricht dem anderen und
expr IN (<set>)
führt auch zu demselben Abfrageplanexpr = ANY(<set>)
, der einen einfachen Index verwenden kann. Einzelheiten:Infolgedessen sind die folgenden beiden Abfragen äquivalent und beide können den einfachen Index verwenden
t_a_b_idx
(was auch die Lösung sein kann, wenn Sie versuchen, Ihre Abfrage zur Verwendung des Index zu veranlassen):Oder:
Identisch für beide:
Dies kann jedoch nicht einfach an eine Funktion übergeben werden, da es in Postgres keine "Tabellenvariablen" gibt. Was zu dem Problem führt, mit dem dieses Thema begonnen hat:
Für dieses Problem gibt es verschiedene Problemumgehungen. Eine davon ist die alternative Antwort, die ich dort hinzugefügt habe. Einige andere:
Die zweite Form von jedem ist anders:
ANY
nimmt ein tatsächliches Array an , währendIN
eine durch Kommas getrennte Liste von Werten verwendet .Dies hat unterschiedliche Folgen für die Eingabe der Eingabe. Wie wir im sehen können
EXPLAIN
Ausgabe der Frage sehen können, ist dieses Formular:wird als Abkürzung gesehen für:
Die tatsächlichen ROW-Werte werden verglichen. Postgres ist derzeit nicht intelligent genug, um den Index für den zusammengesetzten Typ zu erkennen
t_row_idx
anwendbar ist. Es wird auch nicht klar, dass der einfache Index ebenfallst_a_b_idx
anwendbar sein sollte.Eine explizite Besetzung hilft, diesen Mangel an Intelligenz zu überwinden:
Das Umsetzen des richtigen Operanden (
::int_pair[]
) ist optional (jedoch aus Gründen der Leistung und zur Vermeidung von Mehrdeutigkeiten vorzuziehen). Sobald der linke Operand einen bekannten Typ hat, wird der rechte Operand vom "anonymen Datensatz" zu einem passenden Typ gezwungen. Erst dann ist der Operator eindeutig definiert. Und Postgres wählt anwendbare Indizes basierend auf dem Operator und der linken Seite aus Operanden aus. Für viele Operatoren, die a definierenCOMMUTATOR
, kann der Abfrageplaner Operanden spiegeln, um den indizierten Ausdruck nach links zu verschieben. Mit demANY
Konstrukt ist das aber nicht möglich .Verbunden:
Gibt es eine Möglichkeit, eine Textspalte mit Regex-Mustern sinnvoll zu indizieren?
.. Werte werden als Elemente genommen und Postgres kann einzelne ganzzahlige Werte vergleichen, wie wir in der
EXPLAIN
Ausgabe noch einmal sehen können:Daher findet Postgres den einfachen Index
t_a_b_idx
verwendet werden kann.Folglich gibt es im Beispiel eine andere Lösung für den speziellen Fall : Da der benutzerdefinierte zusammengesetzte Typ
int_pair
im Beispiel dem Zeilentyp der Tabellet
selbst entspricht, können wir Folgendes vereinfachen:Dann würde diese Abfrage den Index ohne expliziteres Casting verwenden:
In typischen Anwendungsfällen kann der implizit vorhandene Typ der Tabellenzeile jedoch nicht verwendet werden.
quelle
IN(...)
Liste... OR ...
im obigen Fall (vom Planer) in einen Ausdruck übersetzt werdenANY('{...}')
kann, wird sie normalerweise nur in ein Array übersetzt. In den meisten Fällen sind alsoIN
eine Werteliste undANY
ein Array dasselbe.IN(...)
nicht übersetzt werden kann= ANY('{...}')
.