PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

LATERAL JOIN non utilizza l'indice trigram

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:

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.

Il manuale:

Quindi:

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