Dlaczego?
Kwerenda nie może używać indeksu w jednostce głównej. Potrzebujesz indeksu w tabeli lokalizacje , ale ten, który masz, jest w tabeli adresy .
Możesz zweryfikować moje roszczenie, ustawiając:
SET enable_seqscan = off;
(Tylko w Twojej sesji i tylko do debugowania. Nigdy nie używaj go w środowisku produkcyjnym.) Indeks nie byłby droższy niż skanowanie sekwencyjne, po prostu Postgres nie ma możliwości użycia go w zapytaniu .
Na bok:[INNER] JOIN ... ON true to tylko niezręczny sposób powiedzenia CROSS JOIN ...
Dlaczego indeks jest używany po usunięciu ORDER? i LIMIT ?
Ponieważ Postgres może przepisać ten prosty formularz, aby:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
Zobaczysz dokładnie ten sam plan zapytań. (Przynajmniej robię to w moich testach na Postgresie 9.5.)
Rozwiązanie
Potrzebujesz indeksu na locations.postalcode . I podczas korzystania z LIKE lub ILIKE musisz również przynieść zindeksowane wyrażenie (kod pocztowy ) po po lewej stronie operatora. ILIKE jest zaimplementowany z operatorem ~~* a ten operator nie ma KOMUTATORA (logiczna konieczność), więc nie można odwrócić operandów. Szczegółowe wyjaśnienie w tych powiązanych odpowiedziach:
- Czy kolumny tablicy PostgreSQL mogą indeksować?
- PostgreSQL - tablica tekstowa zawiera wartość podobną do
- Czy istnieje sposób na przydatne indeksowanie kolumny tekstowej zawierającej wzorce wyrażeń regularnych?
Rozwiązaniem jest użycie operatora podobieństwa trygramów %
lub jego odwrotność, operator odległości <->
u najbliższego sąsiada zamiast tego zapytanie (każdy jest komutatorem sam dla siebie, więc operandy mogą swobodnie zmieniać miejsca):
SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Znajdź najbardziej podobny kod pocztowy dla każdego adresu , a następnie sprawdź, czy ten kod pocztowy faktycznie pasuje w pełni.
W ten sposób dłuższy kod pocztowy będzie preferowany automatycznie, ponieważ jest bardziej podobny (mniejsza odległość) niż krótszy kod pocztowy to również pasuje.
Pozostaje trochę niepewności. W zależności od możliwych kodów pocztowych mogą wystąpić fałszywe alarmy z powodu pasujących trygramów w innych częściach ciągu. W pytaniu nie ma wystarczających informacji, aby powiedzieć więcej.
Tu , [INNER] JOIN zamiast CROSS JOIN ma sens, ponieważ dodajemy rzeczywisty warunek dołączenia.
A więc:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);