Hvorfor?
Forespørgslen kan ikke bruge indekset på principal. Du skal bruge et indeks på tabellen placeringer
, men den du har er på tabellen adresser
.
Du kan bekræfte mit krav ved at indstille:
SET enable_seqscan = off;
(Kun i din session og kun til fejlfinding. Brug det aldrig i produktionen.) Det er ikke sådan, at indekset ville være dyrere end en sekventiel scanning, der er bare ingen måde for Postgres at bruge det til din forespørgsel overhovedet .
Til side:[INNER] JOIN ... ON true
er bare en akavet måde at sige CROSS JOIN ...
Hvorfor bruges indekset efter fjernelse af ORDER
og LIMIT
?
Fordi Postgres kan omskrive denne simple formular til:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
Du vil se nøjagtig den samme forespørgselsplan. (Det gør jeg i hvert fald i mine test på Postgres 9.5.)
Løsning
Du skal bruge et indeks på locations.postalcode
. Og mens du bruger LIKE
eller ILIKE
du skal også medbringe det indekserede udtryk (postalcode
) til venstre operatørens side. ILIKE
er implementeret med operatøren ~~*
og denne operatør har ingen COMMUTATOR
(en logisk nødvendighed), så det er ikke muligt at vende operander rundt. Detaljeret forklaring i disse relaterede svar:
- Kan PostgreSQL indeksere array-kolonner?
- PostgreSQL - text Array indeholder værdi svarende til
- Er der en måde at indeksere en tekstkolonne, der indeholder regex-mønstre?
En løsning er at bruge trigram-lighedsoperatoren %
eller dets omvendte, afstandsoperatøren <->
i en nærmeste nabo forespørgsel i stedet (hver er kommutator for sig selv, så operander kan skifte plads frit):
SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Find det mest lignende postnummer
for hver adresse
, og kontroller derefter, om det postnummer
faktisk matcher fuldt ud.
På denne måde et længere postnummer
vil blive foretrukket automatisk, da det er mere ens (mindre afstand) end et kortere postnummer
der også matcher.
Der er en smule usikkerhed tilbage. Afhængigt af mulige postnumre kan der være falske positiver på grund af matchende trigrammer i andre dele af strengen. Der er ikke nok information i spørgsmålet til at sige mere.
Her , [INDRE] JOIN
i stedet for CROSS JOIN
giver mening, da vi tilføjer en faktisk forbindelsesbetingelse.
Så:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);