Warum?
Die Abfrage kann den Index nicht grundsätzlich verwenden. Sie benötigen einen Index für die Tabelle locations
, aber die, die Sie haben, befindet sich in der Tabelle addresses
.
Sie können meine Behauptung überprüfen, indem Sie Folgendes festlegen:
SET enable_seqscan = off;
(Nur in Ihrer Sitzung und nur zum Debuggen. Verwenden Sie ihn niemals in der Produktion.) Es ist nicht so, dass der Index teurer wäre als ein sequenzieller Scan, es gibt einfach keine Möglichkeit für Postgres, ihn für Ihre Abfrage überhaupt zu verwenden .
Beiseite:[INNER] JOIN ... ON true
ist nur eine umständliche Art, CROSS JOIN ...
zu sagen
Warum wird der Index nach dem Entfernen von ORDER
verwendet und LIMIT
?
Weil Postgres dieses einfache Formular umschreiben kann zu:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
Sie sehen genau denselben Abfrageplan. (Zumindest mache ich das in meinen Tests auf Postgres 9.5.)
Lösung
Sie benötigen einen Index zu locations.postalcode
. Und während Sie LIKE
verwenden oder ILIKE
Sie müssten auch den indexierten Ausdruck (postalcode
) nach links Seite des Betreibers. ILIKE
wird mit dem Operator ~~*
implementiert und dieser Operator hat keinen COMMUTATOR
(eine logische Notwendigkeit), daher ist es nicht möglich, Operanden umzudrehen. Detaillierte Erklärung in diesen verwandten Antworten:
- Kann PostgreSQL Array-Spalten indizieren?
- PostgreSQL - Text-Array enthält einen ähnlichen Wert wie
- Gibt es eine Möglichkeit, eine Textspalte mit Regex-Mustern sinnvoll zu indizieren?
Eine Lösung ist die Verwendung des Trigramm-Ähnlichkeitsoperators %
oder seine Umkehrung, der Distanzoperator <->
in einem nächsten Nachbarn stattdessen abfragen (jeder ist Kommutator für sich selbst, also können Operanden die Plätze frei tauschen):
SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Finden Sie die ähnlichste postalcode
für jede address
, und prüfen Sie dann, ob diese postalcode
stimmt eigentlich vollständig überein.
So entsteht eine längere postalcode
wird automatisch bevorzugt, da sie ähnlicher (geringere Entfernung) ist als eine kürzere postalcode
das passt auch.
Eine gewisse Unsicherheit bleibt. Abhängig von möglichen Postleitzahlen können aufgrund übereinstimmender Trigramme in anderen Teilen der Zeichenfolge falsch positive Ergebnisse auftreten. Die Frage enthält nicht genügend Informationen, um mehr zu sagen.
Hier , [INNER] JOIN
statt CROSS JOIN
macht Sinn, da wir eine tatsächliche Join-Bedingung hinzufügen.
Also:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);