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