¿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:
- ¿PostgreSQL puede indexar columnas de matriz?
- PostgreSQL - matriz de texto contiene un valor similar a
- ¿Existe alguna manera de indexar de forma útil una columna de texto que contenga patrones de expresiones regulares?
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.
Entonces:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);