A tabela t
possui dois í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)
;
Nenhum índice é usado com o 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
Mas um deles é usado com o 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
Ele usa o índice do registro se o registro for convertido para o tipo correto:
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 que o planejador não usa o índice sem registro para o any
operador, como o usa para o in
operador?
postgresql
index
optimization
postgresql-9.4
execution-plan
Clodoaldo
fonte
fonte
Respostas:
Internamente, existem duas formas separadas de
IN
, bem como para aANY
construção.Um de cada um, tomando um conjunto , é equivalente ao outro e
expr IN (<set>)
também leva ao mesmo plano de consultaexpr = 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):Ou:
Idêntico para ambos:
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 , enquantoIN
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:é visto como uma abreviação para:
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émt_a_b_idx
deve ser aplicável.Um elenco explícito ajuda a superar essa falta de inteligência:
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 aCOMMUTATOR
, o planejador de consultas pode inverter operandos para trazer a expressão indexada para a esquerda. Mas isso não é possível com aANY
construção.Palavras-chave:
Existe uma maneira de indexar utilmente uma coluna de texto contendo padrões regex?
.. 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: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 dat
própria tabela , poderíamos simplificar:Então essa consulta usaria o índice sem mais nenhuma conversão explícita:
Mas casos de uso típicos não poderão utilizar o tipo implicitamente existente da linha da tabela.
fonte
IN(...)
lista curta possa ser traduzida (pelo planejador) em uma... OR ...
expressão no caso acima, geralmente é apenas traduzida paraANY('{...}')
, ou seja, usando uma matriz. Portanto, na maioria dos casos,IN
com uma lista de valores eANY
uma matriz são a mesma coisa.IN(...)
que não pode ser traduzido= ANY('{...}')
.