En interne, il existe deux formes distinctes de IN, ainsi que pour la ANYconstruction.
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: ANYprend un tableau réel , tandis que INprend 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 EXPLAINsortie 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_idxest applicable. Elle ne se rend pas non plus compte que l'indice simple t_a_b_idxdevrait é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 ANYconstruction.
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 EXPLAINune fois de plus dans la sortie:
Filter: ((b = 1) OR (b = 2))
Postgres trouve donc que l'index simple t_a_b_idxpeut être utilisé.
Par conséquent, il y aurait une autre solution pour le cas particulier dans l'exemple : puisque le type composite personnalisé int_pairdans l'exemple se trouve être équivalent au type de ligne de la table telle - 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.