Por quê?
A consulta não pode usar o índice no principal. Você precisaria de um índice na tabela
locations
, mas o que você tem está na tabela addresses
. Você pode verificar minha reivindicação configurando:
SET enable_seqscan = off;
(Somente em sua sessão, e apenas para depuração. Nunca use em produção.) Não é como se o índice fosse mais caro do que uma varredura sequencial, simplesmente não há como o Postgres usá-lo para sua consulta .
Além:
[INNER] JOIN ... ON true
é apenas uma maneira estranha de dizer CROSS JOIN ...
Por que o índice é usado após a remoção de ORDER
e LIMIT
?
Porque o Postgres pode reescrever este formulário simples para:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
Você verá exatamente o mesmo plano de consulta. (Pelo menos eu faço em meus testes no Postgres 9.5.)
Solução
Você precisa de um índice em
locations.postalcode
. E ao usar LIKE
ou ILIKE
você também precisaria trazer a expressão indexada (postalcode
) à esquerda lado do operador. ILIKE
é implementado com o operador ~~*
e este operador não tem COMMUTATOR
(uma necessidade lógica), então não é possível inverter os operandos. Explicação detalhada nestas respostas relacionadas:- O PostgreSQL pode indexar colunas de matriz? a>
- PostgreSQL - array de texto contém valor semelhante a
- Existe uma maneira útil de indexar uma coluna de texto contendo padrões regex?
Uma solução é usar o operador de similaridade de trigramas
%
ou seu inverso, o operador de distância <->
em um vizinho mais próximo query em vez disso (cada um é comutador para si mesmo, então os operandos podem trocar de lugar livremente):SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Encontre o
postalcode
mais semelhante para cada address
e, em seguida, verifique se esse postalcode
realmente corresponde totalmente. Dessa forma, um
postalcode
mais longo será preferido automaticamente, pois é mais semelhante (menor distância) do que um postalcode
mais curto que também combina. Um pouco de incerteza permanece. Dependendo dos códigos postais possíveis, pode haver falsos positivos devido a trigramas correspondentes em outras partes da string. Não há informações suficientes na pergunta para dizer mais.
Aqui ,
[INNER] JOIN
em vez de CROSS JOIN
faz sentido, já que adicionamos uma condição de junção real. O manual:
Então:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);