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);