PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

LATERAL JOIN verwendet keinen Trigram-Index

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:

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.

Das Handbuch:

Also:

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