Intern gibt es zwei getrennte Formen von IN
sowie für das ANY
Konstrukt.
Einer von beiden, der eine Menge nimmt , entspricht dem anderen und expr IN (<set>)
führt auch zu demselben Abfrageplan expr = 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):
EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));
Oder:
...
WHERE (a,b) IN (VALUES (1,1),(1,2));
Identisch für beide:
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.33..16.71 rows=1 width=8) (actual time=0.101..0.101 rows=0 loops=1)
-> Unique (cost=0.04..0.05 rows=2 width=8) (actual time=0.068..0.070 rows=2 loops=1)
-> Sort (cost=0.04..0.04 rows=2 width=8) (actual time=0.067..0.068 rows=2 loops=1)
Sort Key: "*VALUES*".column1, "*VALUES*".column2
Sort Method: quicksort Memory: 25kB
-> Values Scan on "*VALUES*" (cost=0.00..0.03 rows=2 width=8) (actual time=0.005..0.005 rows=2 loops=1)
-> Index Only Scan using t_plain_idx on t (cost=0.29..8.32 rows=1 width=8) (actual time=0.009..0.009 rows=0 loops=2)
Index Cond: ((a = "*VALUES*".column1) AND (b = "*VALUES*".column2))
Heap Fetches: 0
Planning time: 4.080 ms
Execution time: 0.202 ms
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önnenEXPLAIN
Ausgabe der Frage sehen können, ist dieses Formular:
WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);
wird als Abkürzung gesehen für:
ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])
Die tatsächlichen ROW-Werte werden verglichen. Postgres ist derzeit nicht intelligent genug, um den Index für den zusammengesetzten Typ zu erkennent_row_idx
anwendbar ist. Es wird auch nicht klar, dass der einfache Index ebenfalls t_a_b_idx
anwendbar sein sollte.
Eine explizite Besetzung hilft, diesen Mangel an Intelligenz zu überwinden:
WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);
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 definieren COMMUTATOR
, kann der Abfrageplaner Operanden spiegeln, um den indizierten Ausdruck nach links zu verschieben. Mit dem ANY
Konstrukt ist das aber nicht möglich .
Verbunden:
.. Werte werden als Elemente genommen und Postgres kann einzelne ganzzahlige Werte vergleichen, wie wir in der EXPLAIN
Ausgabe noch einmal sehen können:
Filter: ((b = 1) OR (b = 2))
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 Tabelle t
selbst entspricht, können wir Folgendes vereinfachen:
CREATE INDEX t_row_idx2 ON t ((t));
Dann würde diese Abfrage den Index ohne expliziteres Casting verwenden:
EXPLAIN ANALYZE
SELECT *
FROM t
WHERE t = ANY(ARRAY[(1,1),(1,2)]);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on t (cost=40.59..496.08 rows=1000 width=8) (actual time=0.19
1..0.191 rows=0 loops=1)
Recheck Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
-> Bitmap Index Scan on t_row_idx2 (cost=0.00..40.34 rows=1000 width=0) (actual time=0.188..0.188 rows=0 loops=1)
Index Cond: (t.* = ANY (ARRAY[ROW(1, 1), ROW(1, 2)]))
Planning time: 2.575 ms
Execution time: 0.267 ms
In typischen Anwendungsfällen kann der implizit vorhandene Typ der Tabellenzeile jedoch nicht verwendet werden.