La table t
a deux index:
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)
;
Aucun index n'est utilisé avec l' any
opérateur:
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
Mais l'un d'eux est utilisé avec l' in
opérateur:
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
Il utilise l'index d'enregistrement si l'enregistrement est converti dans le type correct:
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
Pourquoi le planificateur n'utilise-t-il pas l'index non enregistré pour l' any
opérateur comme il l'utilise pour l' in
opérateur?
Réponses:
En interne, il existe deux formes distinctes de
IN
, ainsi que pour laANY
construction.L'un de chacun, prenant un ensemble , est équivalent à l'autre et
expr IN (<set>)
conduit également au même plan de requête queexpr = ANY(<set>)
celui qui peut utiliser un index simple. Détails:Par conséquent, les deux requêtes suivantes sont équivalentes et les deux peuvent utiliser l'index brut
t_a_b_idx
(qui peut également être la solution si vous essayez d'obtenir que votre requête utilise l'index):Ou:
Identique pour les deux:
Cependant , cela ne peut pas être facilement transmis à une fonction, car il n'y a pas de "variables de table" dans Postgres. Ce qui conduit au problème à l'origine de ce sujet:
Il existe différentes solutions de contournement pour ce problème. L'une étant la réponse alternative que j'ai ajoutée ici. Quelques autres:
La deuxième forme de chacun est différente:
ANY
prend un tableau réel , tandis queIN
prend une liste de valeurs séparées par des virgules .Cela a des conséquences différentes pour taper l'entrée. Comme nous pouvons le voir dans la
EXPLAIN
sortie de la question, ce formulaire:est considéré comme un raccourci pour:
Et les valeurs réelles de ROW sont comparées. Postgres n'est pas actuellement assez intelligent pour voir que l'index sur le type composite
t_row_idx
est applicable. Elle ne se rend pas non plus compte que l'indice simplet_a_b_idx
devrait également s'appliquer.Une distribution explicite aide à surmonter ce manque d'intelligence:
La conversion de l'opérande droit (
::int_pair[]
) est facultative (bien que préférable pour les performances et pour éviter les ambiguïtés). Une fois que l'opérande gauche a un type bien connu, l'opérande droit est contraint de "l'enregistrement anonyme" à un type correspondant. Alors seulement, l'opérateur est défini sans ambiguïté. Et Postgres sélectionne les index applicables en fonction de l' opérateur et de l' opérande de gauche . Pour de nombreux opérateurs qui définissent unCOMMUTATOR
, le planificateur de requêtes peut inverser les opérandes pour amener l'expression indexée vers la gauche. Mais ce n'est pas possible avec laANY
construction.En relation:
Existe-t-il un moyen d'indexer utilement une colonne de texte contenant des motifs d'expression régulière?
.. les valeurs sont prises comme éléments et Postgres est capable de comparer les valeurs entières individuelles comme nous pouvons le voir
EXPLAIN
une fois de plus dans la sortie:Postgres trouve donc que l'index simple
t_a_b_idx
peut être utilisé.Par conséquent, il y aurait une autre solution pour le cas particulier dans l'exemple : puisque le type composite personnalisé
int_pair
dans l'exemple se trouve être équivalent au type de ligne de la tablet
elle - même, nous pourrions simplifier:Ensuite, cette requête utiliserait l'index sans conversion plus explicite:
Mais les cas d'utilisation typiques ne pourront pas utiliser le type implicitement existant de la ligne de table.
la source
IN(...)
liste peut être traduite (par le planificateur) en une... OR ...
expression dans le cas ci-dessus, elle est généralement traduite enANY('{...}')
, c'est-à-dire en utilisant un tableau. Donc, dans la plupart des cas,IN
avec une liste de valeurs etANY
avec un tableau, c'est la même chose.IN(...)
ne peut pas être traduit= ANY('{...}')
.