En interne, il existe deux formes distinctes de IN
, ainsi que pour la ANY
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 que expr = 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):
EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));
Ou:
...
WHERE (a,b) IN (VALUES (1,1),(1,2));
Identique pour les deux:
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
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 que IN
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:
WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);
est considéré comme un raccourci pour:
ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])
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 simple t_a_b_idx
devrait également s'appliquer.
Une distribution explicite aide à surmonter ce manque d'intelligence:
WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);
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 un COMMUTATOR
, 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 la ANY
construction.
En relation:
.. 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:
Filter: ((b = 1) OR (b = 2))
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 table t
elle - même, nous pourrions simplifier:
CREATE INDEX t_row_idx2 ON t ((t));
Ensuite, cette requête utiliserait l'index sans conversion plus explicite:
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
Mais les cas d'utilisation typiques ne pourront pas utiliser le type implicitement existant de la ligne de table.