Proč?
Dotaz nemůže použít index na principu. Budete potřebovat index v tabulce locations
, ale ten, který máte, je v tabulce addresses
.
Můj nárok můžete ověřit nastavením:
SET enable_seqscan = off;
(Pouze ve vaší relaci a pouze pro ladění. Nikdy to nepoužívejte v produkci.) Není to tak, že by index byl dražší než sekvenční skenování, prostě neexistuje žádný způsob, jak ho Postgres pro váš dotaz vůbec použít .
Stranou:[INNER] JOIN ... ON true
je jen nepříjemný způsob, jak říct CROSS JOIN ...
Proč se po odstranění ORDER
používá index a LIMIT
?
Protože Postgres umí tento jednoduchý formulář přepsat na:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
Uvidíte přesně stejný plán dotazů. (Aspoň já to dělám ve svých testech na Postgres 9.5.)
Řešení
Potřebujete index na locations.postalcode
. A při používání ILIKE
nebo ILIKE
budete také muset přinést indexovaný výraz (postalcode
) doleva straně operátora. ILIKE
je implementován operátorem ~~*
a tento operátor nemá COMMUTATOR
(logická nutnost), takže není možné operandy obracet. Podrobné vysvětlení v těchto souvisejících odpovědích:
- Umí PostgreSQL indexovat sloupce pole?
- PostgreSQL – textové pole obsahuje hodnotu podobnou
- Existuje způsob, jak užitečně indexovat textový sloupec obsahující vzory regulárních výrazů?
Řešením je použít trigramový operátor podobnosti %
nebo jeho inverzní, operátor vzdálenosti <->
u nejbližšího souseda dotaz (každý je komutátor sám pro sebe, takže operandy mohou libovolně přepínat místa):
SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Najděte nejpodobnější postalcode
pro každou address
a poté zkontrolujte, zda postalcode
ve skutečnosti plně odpovídá.
Tímto způsobem delší postalcode
bude preferováno automaticky, protože je podobnější (menší vzdálenost) než kratší postalcode
to také odpovídá.
Zůstává trochu nejistoty. V závislosti na možných poštovních směrovacích číslech se mohou vyskytnout falešné poplachy kvůli odpovídajícím trigramům v jiných částech řetězce. V otázce není dostatek informací, abych řekl více.
Zde , [INNER] JOIN
místo CROSS JOIN
dává smysl, protože přidáváme skutečnou podmínku spojení.
Takže:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);