PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

LATERAL JOIN não usando índice trigrama

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:

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