Tabela t
ma dwa indeksy:
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)
;
Z any
operatorem nie jest używany indeks :
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
Ale jeden z nich jest używany z in
operatorem:
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
Używa indeksu rekordu, jeśli rekord jest rzutowany na poprawny typ:
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
Dlaczego planista nie używa indeksu nie rekordu dla any
operatora, ponieważ używa go dla in
operatora?
Odpowiedzi:
Wewnętrznie są dwa oddzielne formy z
IN
, jak również dlaANY
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):Lub:
Identyczne dla obu:
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ę , aIN
przyjmuje listę wartości oddzieloną przecinkami .Ma to różne konsekwencje dla wpisywania danych wejściowych. Jak widzimy w
EXPLAIN
wynikach pytania, ten formularz:jest postrzegany jako skrót dla:
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 indekst_a_b_idx
powinien również mieć zastosowanie.Wyraźna obsada pomaga przezwyciężyć ten brak inteligencji:
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ą aCOMMUTATOR
, narzędzie do planowania zapytań może odwrócić operandy, aby przesunąć indeksowane wyrażenie w lewo. Ale nie jest to możliwe w przypadkuANY
konstrukcji.Związane z:
Czy istnieje sposób na użyteczne indeksowanie kolumny tekstowej zawierającej wzorce wyrażeń regularnych?
.. 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: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 wierszat
samej tabeli , możemy uprościć:Następnie to zapytanie użyłoby indeksu bez wyraźniejszego rzutowania:
Ale typowe przypadki użycia nie będą w stanie wykorzystać domyślnie istniejącego typu wiersza tabeli.
źródło
IN(...)
lista może zostać przetłumaczona (przez planistę) na... OR ...
wyrażenie w powyższym przypadku, zwykle jest to po prostu przetłumaczone naANY('{...}')
, to znaczy za pomocą tablicy. W większości przypadkówIN
lista wartości iANY
tablica są tym samym.IN(...)
nie można go przetłumaczyć= ANY('{...}')
.