La tabla t
tiene dos índices:
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)
;
No se utiliza índice con el any
operador:
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
Pero uno de ellos se usa con el in
operador:
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
Utiliza el índice de registro si el registro se convierte al tipo correcto:
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
¿Por qué el planificador no usa el índice sin registro para el any
operador como lo usa para el in
operador?
Respuestas:
Internamente, hay dos formas separadas de
IN
, así como para laANY
construcción.Uno de cada uno, tomando un conjunto , es equivalente al otro y
expr IN (<set>)
también conduce al mismo plan de consultaexpr = 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):O:
Idéntico para ambos:
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 queIN
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:es visto como una forma abreviada de:
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ént_a_b_idx
debería ser aplicable.Un reparto explícito ayuda a superar esta falta de inteligencia:
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 aCOMMUTATOR
, el planificador de consultas puede voltear operandos para llevar la expresión indexada a la izquierda. Pero eso no es posible con laANY
construcción.Relacionado:
¿Hay alguna manera de indexar útilmente una columna de texto que contenga patrones de expresiones regulares?
.. los valores se toman como elementos y Postgres puede comparar valores enteros individuales como podemos ver en la
EXPLAIN
salida una vez más: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 ent
sí, podríamos simplificar:Luego, esta consulta usaría el índice sin más conversión explícita:
Pero los casos de uso típicos no podrán utilizar el tipo implícitamente existente de la fila de la tabla.
fuente
IN(...)
lista corta puede ser traducida (por el planificador) a una... OR ...
expresión en el caso anterior, generalmente solo se traduceANY('{...}')
, es decir, usando una matriz. Entonces, en la mayoría de los casos,IN
con una lista de valores yANY
con una matriz son lo mismo.IN(...)
que no se puede traducir= ANY('{...}')
.