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 :
- Est-ce que PostgreSQL peut indexer des colonnes de tableau ?
- PostgreSQL - tableau de texte contient une valeur similaire à
- Existe-t-il un moyen d'indexer utilement une colonne de texte contenant des motifs regex ?
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.
Donc :
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);