Wewnętrznie są dwa oddzielne formy z IN
, jak również dla ANY
konstruktu.
Jeden z nich, biorąc zestaw , jest równoważny drugiemu i expr IN (<set>)
również prowadzi do tego samego planu zapytań, expr = ANY(<set>)
który może wykorzystywać zwykły indeks. Detale:
W związku z tym następujące dwa zapytania są równoważne i oba mogą korzystać ze zwykłego indeksu t_a_b_idx
(co może być również rozwiązaniem, jeśli próbujesz uzyskać zapytanie w celu użycia tego indeksu):
EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));
Lub:
...
WHERE (a,b) IN (VALUES (1,1),(1,2));
Identyczne dla obu:
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
Jednak nie można tego łatwo przekazać do funkcji, ponieważ w Postgres nie ma „zmiennych tabeli”. Co prowadzi do problemu, który rozpoczął ten temat:
Istnieją różne obejścia tego problemu. Jednym z nich jest alternatywna odpowiedź, którą tam dodałem. Jacyś inni:
Druga forma każdego z nich jest inna: ANY
przyjmuje rzeczywistą tablicę , a IN
przyjmuje listę wartości oddzieloną przecinkami .
Ma to różne konsekwencje dla wpisywania danych wejściowych. Jak widzimy w EXPLAIN
wynikach pytania, ten formularz:
WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);
jest postrzegany jako skrót dla:
ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])
Rzeczywiste wartości ROW są porównywane. Postgres nie jest obecnie wystarczająco inteligentny, aby sprawdzić, czy ma zastosowanie indeks typu złożonego t_row_idx
. Nie zdaje sobie również sprawy, że prosty indeks t_a_b_idx
powinien również mieć zastosowanie.
Wyraźna obsada pomaga przezwyciężyć ten brak inteligencji:
WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);
Rzutowanie właściwego operandu ( ::int_pair[]
) jest opcjonalne (chociaż jest preferowane ze względu na wydajność i aby uniknąć niejednoznaczności). Gdy lewy operand ma dobrze znany typ, prawy operand jest wymuszany z „anonimowego rekordu” na pasujący typ. Dopiero wtedy operator jest definiowany jednoznacznie. A Postgres wybiera odpowiednie indeksy na podstawie operatora i lewego operandu. W przypadku wielu operatorów, które definiują a COMMUTATOR
, narzędzie do planowania zapytań może odwrócić operandy, aby przesunąć indeksowane wyrażenie w lewo. Ale nie jest to możliwe w przypadku ANY
konstrukcji.
Związane z:
.. wartości są traktowane jako elementy, a Postgres jest w stanie porównać poszczególne wartości całkowite, co możemy zobaczyć w danych EXPLAIN
wyjściowych jeszcze raz:
Filter: ((b = 1) OR (b = 2))
Dlatego Postgres stwierdza, że t_a_b_idx
można użyć prostego indeksu .
W związku z tym istnieje inne rozwiązanie dla konkretnego przypadku w przykładzie : ponieważ niestandardowy typ złożonego int_pair
w przykładzie okazuje się być równoważny typowi wiersza t
samej tabeli , możemy uprościć:
CREATE INDEX t_row_idx2 ON t ((t));
Następnie to zapytanie użyłoby indeksu bez wyraźniejszego rzutowania:
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
Ale typowe przypadki użycia nie będą w stanie wykorzystać domyślnie istniejącego typu wiersza tabeli.