Internamente, hay dos formas separadas de IN
, así como para la ANY
construcción.
Uno de cada uno, tomando un conjunto , es equivalente al otro y expr IN (<set>)
también conduce al mismo plan de consulta expr = ANY(<set>)
que puede usar un índice simple. Detalles:
En consecuencia, las siguientes dos consultas son equivalentes y ambas pueden usar el índice simple t_a_b_idx
(que también puede ser la solución si está intentando que su consulta use el índice):
EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));
O:
...
WHERE (a,b) IN (VALUES (1,1),(1,2));
Idéntico para ambos:
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
Sin embargo , esto no se puede pasar fácilmente a una función, ya que no hay "variables de tabla" en Postgres. Lo que lleva al problema que inició este tema:
Hay varias soluciones para ese problema. Una es la respuesta alternativa que agregué allí. Algunos otros:
La segunda forma de cada uno es diferente: ANY
toma una matriz real , mientras que IN
toma una lista de valores separados por comas .
Esto tiene diferentes consecuencias para escribir la entrada. Como podemos ver en el EXPLAIN
resultado de la pregunta, este formulario:
WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);
es visto como una forma abreviada de:
ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])
Y se comparan los valores reales de ROW. Postgres no es lo suficientemente inteligente como para ver que el índice en el tipo compuesto t_row_idx
es aplicable. Tampoco se da cuenta de que el índice simple también t_a_b_idx
debería ser aplicable.
Un reparto explícito ayuda a superar esta falta de inteligencia:
WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);
Lanzar el operando correcto ( ::int_pair[]
) es opcional (aunque preferible para el rendimiento y para evitar ambigüedades). Una vez que el operando izquierdo tiene un tipo bien conocido, el operando derecho se convierte de "registro anónimo" a un tipo coincidente. Solo entonces, el operador se define inequívocamente. Y Postgres elige índices aplicables basados en el operador y el operando izquierdo . Para muchos operadores que definen a COMMUTATOR
, el planificador de consultas puede voltear operandos para llevar la expresión indexada a la izquierda. Pero eso no es posible con la ANY
construcción.
Relacionado:
.. los valores se toman como elementos y Postgres puede comparar valores enteros individuales como podemos ver en la EXPLAIN
salida una vez más:
Filter: ((b = 1) OR (b = 2))
Por lo tanto, Postgres encuentra que t_a_b_idx
se puede usar el índice simple .
En consecuencia, habría otra solución para el caso particular en el ejemplo : dado que el tipo compuesto personalizado int_pair
en el ejemplo es equivalente al tipo de fila de la tabla en t
sí, podríamos simplificar:
CREATE INDEX t_row_idx2 ON t ((t));
Luego, esta consulta usaría el índice sin más conversión explícita:
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
Pero los casos de uso típicos no podrán utilizar el tipo implícitamente existente de la fila de la tabla.