Varför?
Frågan kan inte använda indexet på principal. Du skulle behöva ett index på tabellen platser
, men den du har finns i tabellen adresser
.
Du kan verifiera mitt anspråk genom att ställa in:
SET enable_seqscan = off;
(Endast i din session och endast för felsökning. Använd det aldrig i produktionen.) Det är inte som att indexet skulle vara dyrare än en sekventiell skanning, det finns helt enkelt inget sätt för Postgres att använda det för din fråga överhuvudtaget .
Bortsett från:[INNER] JOIN ... ON true
är bara ett besvärligt sätt att säga CROSS JOIN ...
Varför används indexet efter att ORDER
tagits bort och LIMIT
?
Eftersom Postgres kan skriva om detta enkla formulär till:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
Du kommer att se exakt samma frågeplan. (Åtminstone gör jag det i mina tester på Postgres 9.5.)
Lösning
Du behöver ett index på locations.postalcode
. Och medan du använder LIKE
eller ILIKE
du skulle också behöva ta med det indexerade uttrycket (postalcode
) till vänster operatörens sida. ILIKE
implementeras med operatorn ~~*
och den här operatören har ingen COMMUTATOR
(en logisk nödvändighet), så det är inte möjligt att vända på operander. Detaljerad förklaring i dessa relaterade svar:
- Kan PostgreSQL indexera arraykolumner?
- PostgreSQL - text Array innehåller värde som liknar
- Finns det något sätt att på ett användbart sätt indexera en textkolumn som innehåller regexmönster?
En lösning är att använda trigramlikhetsoperatorn %
eller dess invers, distansoperatören <->
i en närmaste granne fråga istället (var och en är kommutator för sig själv, så operander kan byta plats fritt):
SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Hitta det mest liknande postnumret
för varje adress
, och kontrollera sedan om det postnumret
matchar faktiskt helt.
På så sätt ett längre postnummer
kommer att föredras automatiskt eftersom det är mer likt (mindre avstånd) än ett kortare postnummer
som också matchar.
Lite osäkerhet kvarstår. Beroende på möjliga postnummer kan det finnas falska positiva resultat på grund av matchande trigram i andra delar av strängen. Det finns inte tillräckligt med information i frågan för att säga mer.
Här , [INNER] JOIN
istället för CROSS JOIN
vettigt, eftersom vi lägger till ett faktiskt sammanfogningsvillkor.
Så:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);