Waarom?
De query kan de index op principal niet gebruiken. Je hebt een index nodig op de tabel locations
, maar degene die je hebt staat op de tafel addresses
.
U kunt mijn claim verifiëren door het volgende in te stellen:
SET enable_seqscan = off;
(Alleen in uw sessie en alleen voor debuggen. Gebruik het nooit in productie.) Het is niet alsof de index duurder zou zijn dan een sequentiële scan, Postgres kan het gewoon helemaal niet gebruiken voor uw zoekopdracht .
Terzijde:[INNER] JOIN ... ON true
is gewoon een ongemakkelijke manier om CROSS JOIN ...
. te zeggen
Waarom wordt de index gebruikt na het verwijderen van ORDER
en LIMIT
?
Omdat Postgres deze eenvoudige vorm kan herschrijven tot:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
U ziet exact hetzelfde queryplan. (Tenminste dat doe ik in mijn tests op Postgres 9.5.)
Oplossing
Je hebt een index nodig op locations.postalcode
. En tijdens het gebruik van LIKE
of ILIKE
je zou ook de geïndexeerde uitdrukking (postalcode
) naar links kant van de bediener. ILIKE
wordt geïmplementeerd met de operator ~~*
en deze operator heeft geen COMMUTATOR
(een logische noodzaak), dus het is niet mogelijk om operanden om te draaien. Gedetailleerde uitleg in deze gerelateerde antwoorden:
- Kunnen PostgreSQL matrixkolommen indexeren?
- PostgreSQL - tekstarray bevat waarde vergelijkbaar met
- Is er een manier om een tekstkolom met regex-patronen te indexeren?
Een oplossing is om de trigram-overeenkomstoperator %
of het omgekeerde, de afstandsoperator <->
in een dichtstbijzijnde buur query in plaats daarvan (elk is een commutator voor zichzelf, dus operanden kunnen vrij van plaats wisselen):
SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Vind de meest gelijkende postalcode
voor elk address
, en controleer dan of die postalcode
komt eigenlijk volledig overeen.
Op deze manier een langere postalcode
krijgt automatisch de voorkeur omdat het meer op elkaar lijkt (kleinere afstand) dan een kortere postalcode
dat komt ook overeen.
Er blijft een beetje onzekerheid. Afhankelijk van mogelijke postcodes, kunnen er valse positieven zijn als gevolg van overeenkomende trigrammen in andere delen van de tekenreeks. Er is niet genoeg informatie in de vraag om meer te zeggen.
Hier , [INNER] JOIN
in plaats van CROSS JOIN
is logisch, aangezien we een daadwerkelijke deelnamevoorwaarde toevoegen.
Dus:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);