Internamente, existem duas formas separadas de IN
, bem como para a ANY
construção.
Um de cada um, tomando um conjunto , é equivalente ao outro e expr IN (<set>)
também leva ao mesmo plano de consulta expr = ANY(<set>)
que pode usar um índice simples. Detalhes:
Conseqüentemente, as duas consultas a seguir são equivalentes e ambas podem usar o índice simples t_a_b_idx
(que também pode ser a solução se você estiver tentando fazer com que sua consulta use o índice):
EXPLAIN ANALYZE
SELECT *
FROM t
WHERE (a,b) = ANY(VALUES (1,1),(1,2));
Ou:
...
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
No entanto , isso não pode ser facilmente transmitido para uma função, pois não existem "variáveis de tabela" no Postgres. O que leva ao problema que iniciou este tópico:
Existem várias soluções alternativas para esse problema. Uma sendo a resposta alternativa que adicionei lá. Alguns outros:
A segunda forma de cada um é diferente: ANY
pega uma matriz real , enquanto IN
pega uma lista de valores separados por vírgula .
Isso tem consequências diferentes para digitar a entrada. Como podemos ver na EXPLAIN
saída da pergunta, este formulário:
WHERE (a,b) = ANY(ARRAY[(1,1),(1,2)]);
é visto como uma abreviação para:
ROW(a, b) = ANY (ARRAY[ROW(1, 1), ROW(1, 2)])
E os valores reais de ROW são comparados. Atualmente, o Postgres não é inteligente o suficiente para ver se o índice no tipo composto t_row_idx
é aplicável. Nem percebe que o índice simples também t_a_b_idx
deve ser aplicável.
Um elenco explícito ajuda a superar essa falta de inteligência:
WHERE (a,b)::int_pair = ANY(ARRAY[(1,1),(1,2)]::int_pair[]);
A transmissão do operando certo ( ::int_pair[]
) é opcional (embora seja preferível para desempenho e para evitar ambiguidades). Depois que o operando esquerdo tiver um tipo conhecido, o operando direito será coagido de "registro anônimo" para um tipo correspondente. Somente então, o operador é definido sem ambiguidade. O Postgres escolhe os índices aplicáveis com base no operador e no operando esquerdo . Para muitos operadores que definem a COMMUTATOR
, o planejador de consultas pode inverter operandos para trazer a expressão indexada para a esquerda. Mas isso não é possível com a ANY
construção.
Palavras-chave:
.. os valores são tomados como elementos e o Postgres é capaz de comparar valores inteiros individuais, como podemos ver na EXPLAIN
saída mais uma vez:
Filter: ((b = 1) OR (b = 2))
Portanto, o Postgres considera que o índice simples t_a_b_idx
pode ser usado.
Consequentemente, haveria outra solução para o caso específico no exemplo : como o tipo composto personalizado int_pair
no exemplo é equivalente ao tipo de linha da t
própria tabela , poderíamos simplificar:
CREATE INDEX t_row_idx2 ON t ((t));
Então essa consulta usaria o índice sem mais nenhuma conversão 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
Mas casos de uso típicos não poderão utilizar o tipo implicitamente existente da linha da tabela.