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