Mengapa?
Kueri tidak dapat menggunakan indeks pada prinsipal. Anda akan membutuhkan indeks pada tabel locations
, tetapi yang Anda miliki ada di tabel addresses
.
Anda dapat memverifikasi klaim saya dengan menyetel:
SET enable_seqscan = off;
(Hanya dalam sesi Anda, dan hanya untuk debugging. Jangan pernah menggunakannya dalam produksi.) Ini tidak seperti indeks akan lebih mahal daripada pemindaian berurutan, tidak ada cara bagi Postgres untuk menggunakannya untuk kueri Anda sama sekali .
Selain:[INNER] JOIN ... ON true
hanyalah cara yang canggung untuk mengatakan CROSS JOIN ...
Mengapa indeks digunakan setelah menghapus ORDER
dan LIMIT
?
Karena Postgres dapat menulis ulang formulir sederhana ini menjadi:
SELECT *
FROM addresses a
JOIN locations l ON a.address ILIKE '%' || l.postalcode || '%';
Anda akan melihat paket kueri yang sama persis. (Setidaknya saya lakukan dalam pengujian saya di Postgres 9.5.)
Solusi
Anda memerlukan indeks di locations.postalcode
. Dan saat menggunakan LIKE
atau ILIKE
anda juga perlu membawa ekspresi terindeks (postalcode
) ke kiri sisi operator. ILIKE
diimplementasikan dengan operator ~~*
dan operator ini tidak memiliki COMMUTATOR
(kebutuhan logis), jadi tidak mungkin untuk membalik operan. Penjelasan terperinci dalam jawaban terkait ini:
- Dapatkah PostgreSQL mengindeks kolom array?
- PostgreSQL - teks Array berisi nilai yang mirip dengan
- Apakah ada cara yang berguna untuk mengindeks kolom teks yang berisi pola ekspresi reguler?
Solusinya adalah dengan menggunakan operator kesamaan trigram %
atau kebalikannya, operator jarak <->
di tetangga terdekat query sebagai gantinya (masing-masing adalah komutator untuk dirinya sendiri, sehingga operan dapat berpindah tempat dengan bebas):
SELECT *
FROM addresses a
JOIN LATERAL (
SELECT *
FROM locations
ORDER BY postalcode <-> a.address
LIMIT 1
) l ON address ILIKE '%' || postalcode || '%';
Temukan postalcode
yang paling mirip untuk setiap addresses
, lalu periksa apakah postalcode
. itu benar-benar cocok sepenuhnya.
Dengan cara ini, postalcode
yang lebih panjang akan lebih disukai secara otomatis karena lebih mirip (jarak lebih kecil) daripada postalcode
yang lebih pendek yang juga cocok.
Sedikit ketidakpastian tetap ada. Tergantung pada kemungkinan kode pos, mungkin ada positif palsu karena trigram yang cocok di bagian lain dari string. Tidak ada informasi yang cukup dalam pertanyaan untuk mengatakan lebih banyak.
Disini , [INNER] JOIN
alih-alih CROSS JOIN
masuk akal, karena kami menambahkan kondisi gabungan yang sebenarnya.
Jadi:
CREATE INDEX locations_postalcode_trgm_gist_idx ON locations
USING gist (postalcode gist_trgm_ops);