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);