Perché?
La query non può utilizzare l'indice sull'entità. Avresti bisogno di un indice sulla tabella locations , ma quello che hai è nella tabella addresses .
Puoi verificare la mia richiesta impostando:
SET enable_seqscan = off;
(Solo nella tua sessione e solo per il debug. Non usarlo mai in produzione.) Non è che l'indice sarebbe più costoso di una scansione sequenziale, semplicemente non c'è modo per Postgres di usarlo per la tua query .
A parte:[INNER] JOIN ... ON true è solo un modo imbarazzante per dire CROSS JOIN ...
Perché l'indice viene utilizzato dopo aver rimosso ORDER e LIMIT ?
Perché Postgres può riscrivere questo semplice modulo in:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
Vedrai lo stesso identico piano di query. (Almeno lo faccio nei miei test su Postgres 9.5.)
Soluzione
Hai bisogno di un indice su locations.postalcode . E mentre usi LIKE o LIKE dovresti anche portare l'espressione indicizzata (postalcode ) a sinistra lato dell'operatore. LIKE è implementato con l'operatore ~~* e questo operatore non ha COMMUTATOR (una necessità logica), quindi non è possibile capovolgere gli operandi. Spiegazione dettagliata in queste risposte correlate:
- PostgreSQL può indicizzare colonne array?
- PostgreSQL - array di testo contiene un valore simile a
- C'è un modo per indicizzare in modo utile una colonna di testo contenente pattern regex?
Una soluzione consiste nell'usare l'operatore di similarità del trigramma %
o il suo inverso, l'operatore di distanza <->
in un vicino più vicino query invece (ciascuno è commutatore per se stesso, quindi gli operandi possono cambiare posizione liberamente):
SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Trova il postalcode più simile per ogni address , quindi controlla se quel postalcode effettivamente corrisponde completamente.
In questo modo, un postalcode più lungo verrà preferito automaticamente poiché è più simile (distanza inferiore) rispetto a un postalcode più breve anche questo corrisponde.
Resta un po' di incertezza. A seconda dei possibili codici postali, potrebbero esserci falsi positivi a causa della corrispondenza dei trigrammi in altre parti della stringa. Non ci sono abbastanza informazioni nella domanda per aggiungere altro.
Qui , [INNER] JOIN invece di CROSS JOIN ha senso, poiché aggiungiamo una condizione di unione effettiva.
Quindi:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);