PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

LATERAL JOIN n'utilise pas l'index de trigramme

Pourquoi ?

La requête ne peut pas utiliser l'index sur le principal. Vous auriez besoin d'un index sur la table locations , mais celle que vous avez est sur la table addresses .

Vous pouvez vérifier ma demande en définissant :

SET enable_seqscan = off;

(Dans votre session uniquement et pour le débogage uniquement. Ne l'utilisez jamais en production.) Ce n'est pas comme si l'index serait plus cher qu'une analyse séquentielle, il n'y a tout simplement aucun moyen pour Postgres de l'utiliser pour votre requête du tout .

À part :[INNER] JOIN ... ON true est juste une façon maladroite de dire CROSS JOIN ...

Pourquoi l'index est-il utilisé après la suppression de ORDER ? et LIMIT ?

Parce que Postgres peut réécrire ce formulaire simple en :

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

Vous verrez exactement le même plan de requête. (Du moins je le fais dans mes tests sur Postgres 9.5.)

Solution

Vous avez besoin d'un index sur locations.postalcode . Et en utilisant LIKE ou ILIKE vous devrez également apporter l'expression indexée (postalcode ) à gauche côté de l'opérateur. ILIKE est implémenté avec l'opérateur ~~* et cet opérateur n'a pas de COMMUTATOR (une nécessité logique), il n'est donc pas possible d'inverser les opérandes. Explication détaillée dans ces réponses associées :

Une solution consiste à utiliser l'opérateur de similarité trigramme % ou son inverse, l'opérateur de distance <-> chez un voisin le plus proche requête à la place (chacun est un commutateur pour lui-même, donc les opérandes peuvent changer de place librement) :

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

Rechercher le postalcode le plus similaire pour chaque address , puis vérifiez si ce postalcode correspond en fait entièrement.

De cette façon, un postalcode plus long sera préféré automatiquement car il est plus similaire (distance plus petite) qu'un postalcode plus court qui correspond également.

Un peu d'incertitude demeure. Selon les codes postaux possibles, il pourrait y avoir des faux positifs en raison de trigrammes correspondants dans d'autres parties de la chaîne. Il n'y a pas assez d'informations dans la question pour en dire plus.

Ici , [INNER] JOIN au lieu de CROSS JOIN logique, puisque nous ajoutons une condition de jointure réelle.

Le manuel :

Donc :

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