sql >> Base de Datos >  >> RDS >> PostgreSQL

UNIÓN LATERAL sin usar el índice de trigramas

¿Por qué?

La consulta no puede usar el índice en principal. Necesitaría un índice en la tabla locations , pero el que tienes está en la tabla addresses .

Puede verificar mi reclamo configurando:

SET enable_seqscan = off;

(Solo en su sesión, y solo para depuración. Nunca lo use en producción). No es que el índice sea más costoso que un escaneo secuencial, simplemente no hay forma de que Postgres lo use para su consulta en absoluto .

Aparte:[INNER] JOIN ... ON true es solo una forma incómoda de decir CROSS JOIN ...

¿Por qué se usa el índice después de eliminar ORDER? y LIMIT ?

Porque Postgres puede reescribir este formulario simple para:

SELECT *
FROM   addresses a
JOIN   locations l ON a.address ILIKE '%' || l.postalcode || '%';

Verá exactamente el mismo plan de consulta. (Al menos lo hago en mis pruebas en Postgres 9.5.)

Solución

Necesita un índice en locations.postalcode . Y mientras usa LIKE o ILIKE también necesitaría traer la expresión indexada (postalcode ) a la izquierda lado del operador. ILIKE se implementa con el operador ~~* y este operador no tiene COMMUTATOR (una necesidad lógica), por lo que no es posible cambiar los operandos. Explicación detallada en estas respuestas relacionadas:

Una solución es usar el operador de similitud de trigramas % o su inversa, el operador de distancia <-> en un vecino más cercano consulta en su lugar (cada uno es un conmutador por sí mismo, por lo que los operandos pueden cambiar de lugar libremente):

SELECT *
FROM   addresses a
JOIN   LATERAL (
   SELECT *
   FROM   locations
   ORDER  BY postalcode <-> a.address
   LIMIT  1
   ) l ON address ILIKE '%' || postalcode || '%';

Encuentre el postalcode más similar para cada address y luego verifique si ese postalcode en realidad coincide completamente.

De esta forma, un postalcode más largo se preferirá automáticamente ya que es más similar (menor distancia) que un postalcode más corto eso también coincide.

Queda un poco de incertidumbre. Dependiendo de los posibles códigos postales, podría haber falsos positivos debido a la coincidencia de trigramas en otras partes de la cadena. No hay suficiente información en la pregunta para decir más.

Aquí , [INNER] JOIN en lugar de CROSS JOIN tiene sentido, ya que agregamos una condición de unión real.

El manual:

Entonces:

CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);