Saya harus menulis kueri sederhana tempat saya mencari nama orang yang dimulai dengan B atau D:
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
Saya bertanya-tanya apakah ada cara untuk menulis ulang ini menjadi lebih banyak performan. Jadi saya bisa menghindari or
dan / atau like
?
postgresql
performance
index
regular-expression
pattern-matching
Lucas Kauffman
sumber
sumber
s.name
diindeks?name
pun bisa berguna di sini jika Anda peduli dengan kinerja.Jawaban:
Permintaan Anda cukup optimal. Sintaks tidak akan menjadi jauh lebih pendek, permintaan tidak akan menjadi jauh lebih cepat:
Jika Anda benar-benar ingin mempersingkat sintaks , gunakan ekspresi reguler dengan cabang :
Atau sedikit lebih cepat, dengan kelas karakter :
Pengujian cepat tanpa indeks menghasilkan hasil yang lebih cepat daripada
SIMILAR TO
dalam kasus mana pun untuk saya.Dengan indeks B-Tree yang sesuai di tempat,
LIKE
memenangkan perlombaan ini dengan urutan besarnya.Baca dasar-dasar tentang pencocokan pola dalam manual .
Indeks untuk kinerja yang unggul
Jika Anda khawatir dengan kinerja, buat indeks seperti ini untuk tabel yang lebih besar:
Membuat kueri semacam ini lebih cepat dengan perintah besarnya. Pertimbangan khusus berlaku untuk urutan sortir khusus-lokal. Baca lebih lanjut tentang kelas operator di manual . Jika Anda menggunakan lokal "C" standar (kebanyakan orang tidak), indeks biasa (dengan kelas operator default) akan dilakukan.
Indeks semacam itu hanya baik untuk pola berlabuh kiri (cocok dari awal string).
SIMILAR TO
atau ekspresi reguler dengan ekspresi dasar berlabuh kiri dapat menggunakan indeks ini juga. Tetapi tidak dengan cabang(B|D)
atau kelas karakter[BD]
(setidaknya dalam tes saya pada PostgreSQL 9.0).Pencocokan trigram atau pencarian teks menggunakan indeks GIN atau GiST khusus.
Gambaran umum operator pencocokan pola
LIKE
(~~
) Sederhana dan cepat tetapi terbatas dalam kemampuannya.ILIKE
(~~*
) varian case sensitif.pg_trgm memperluas dukungan indeks untuk keduanya.
~
(pencocokan ekspresi reguler) sangat kuat tetapi lebih kompleks dan mungkin lambat untuk apa pun selain ekspresi dasar.SIMILAR TO
tidak ada gunanya . Halfbreed anehLIKE
dan ekspresi reguler. Saya tidak pernah menggunakannya. Lihat di bawah.% adalah operator "kesamaan", yang disediakan oleh modul tambahan
pg_trgm
. Lihat di bawah.@@
adalah operator pencarian teks. Lihat di bawah.pg_trgm - pencocokan trigram
Dimulai dengan PostgreSQL 9.1 Anda dapat memfasilitasi ekstensi
pg_trgm
untuk memberikan dukungan indeks untuk pola / apa saja (dan pola regexp sederhana ) menggunakan indeks GIN atau GiST.LIKE
ILIKE
~
Detail, contoh, dan tautan:
pg_trgm
juga menyediakan operator ini :%
- operator "kesamaan"<%
(komutator%>
:) - operator "word_similarity" di Postgres 9.6 atau lebih baru<<%
(komutator%>>
:) - operator "strict_word_similarity" di Postgres 11 atau lebih baruPencarian Teks
Merupakan jenis khusus pencocokan pola dengan infrastruktur dan tipe indeks terpisah. Ini menggunakan kamus dan stemming dan merupakan alat yang hebat untuk menemukan kata-kata dalam dokumen, terutama untuk bahasa alami.
Pencocokan awalan juga didukung:
Serta pencarian frasa sejak Postgres 9.6:
Pertimbangkan pengantar dalam manual dan ikhtisar operator dan fungsi .
Alat tambahan untuk pencocokan string fuzzy
Modul fuzzystrmatch tambahan menawarkan beberapa opsi lebih banyak, tetapi kinerja umumnya lebih rendah daripada semua yang di atas.
Secara khusus, berbagai implementasi
levenshtein()
fungsi dapat berperan.Mengapa ekspresi reguler (
~
) selalu lebih cepat daripadaSIMILAR TO
?Jawabannya sederhana.
SIMILAR TO
ekspresi ditulis ulang menjadi ekspresi reguler secara internal. Jadi, untuk setiapSIMILAR TO
ekspresi, setidaknya ada satu ekspresi reguler yang lebih cepat (yang menghemat biaya penulisan ulang ekspresi). Tidak ada keuntungan kinerja dalam menggunakanSIMILAR TO
pernah .Dan ekspresi sederhana yang dapat dilakukan dengan
LIKE
(~~
) lebih cepatLIKE
pula.SIMILAR TO
hanya didukung di PostgreSQL karena berakhir pada konsep awal standar SQL. Mereka masih belum menyingkirkannya. Tapi ada rencana untuk menghapusnya dan memasukkan pertandingan regexp - atau begitulah yang saya dengar.EXPLAIN ANALYZE
mengungkapkannya. Coba saja dengan meja apa saja sendiri!Mengungkapkan:
SIMILAR TO
telah ditulis ulang dengan ekspresi reguler (~
).Kinerja terbaik untuk kasus khusus ini
Tetapi
EXPLAIN ANALYZE
mengungkapkan lebih banyak. Coba, dengan indeks yang disebutkan sebelumnya di tempat:Mengungkapkan:
Secara internal, dengan indeks yang tidak locale-sadar (
text_pattern_ops
atau menggunakan lokalC
) ekspresi kiri-berlabuh sederhana yang ditulis ulang dengan operator pola teks ini:~>=~
,~<=~
,~>~
,~<~
. Ini adalah kasus untuk~
,~~
atauSIMILAR TO
sama.Hal yang sama berlaku untuk indeks pada
varchar
tipe denganvarchar_pattern_ops
atauchar
denganbpchar_pattern_ops
.Jadi, diterapkan pada pertanyaan awal, ini adalah cara tercepat yang mungkin :
Tentu saja, jika Anda kebetulan mencari inisial yang berdekatan , Anda dapat menyederhanakan lebih lanjut:
Keuntungan atas penggunaan sederhana
~
atau~~
kecil. Jika kinerja bukan persyaratan utama Anda, Anda harus tetap menggunakan operator standar - sampai pada apa yang sudah Anda miliki dalam pertanyaan.sumber
similar
pemindaian?EXPLAIN ANALYZE
menunjukkan 2 scan indeks bitmap. Beberapa pemindaian indeks bitmap dapat digabungkan dengan lebih cepat.OR
denganUNION ALL
atau menggantiname LIKE 'B%'
denganname >= 'B' AND name <'C'
di Postgres?UNION
tidak akan tetapi, ya, menggabungkan rentang menjadi satuWHERE
klausa akan mempercepat permintaan. Saya telah menambahkan lebih banyak jawaban saya. Tentu saja, Anda harus memperhitungkan lokal Anda. Pencarian sadar-lokal selalu lebih lambat.Bagaimana menambahkan kolom ke tabel. Tergantung pada kebutuhan Anda yang sebenarnya:
PostgreSQL tidak mendukung kolom yang dikomputasi dalam tabel dasar ala SQL Server tetapi kolom baru dapat dipertahankan melalui pemicu. Jelas, kolom baru ini akan diindeks.
Atau, indeks pada ekspresi akan memberi Anda hal yang sama, lebih murah. Misalnya:
Kueri yang cocok dengan ekspresi dalam kondisi mereka dapat memanfaatkan indeks ini.
Dengan cara ini, hit kinerja diambil ketika data dibuat atau diubah, jadi mungkin hanya sesuai untuk lingkungan aktivitas rendah (yaitu menulis lebih sedikit daripada membaca).
sumber
Anda bisa mencoba
Saya tidak tahu apakah ekspresi di atas atau asli Anda masuk dalam Postgres.
Jika Anda membuat indeks yang disarankan juga akan tertarik untuk mendengar bagaimana ini membandingkan dengan opsi lain.
sumber
Apa yang telah saya lakukan di masa lalu, dihadapkan dengan masalah kinerja yang serupa, adalah untuk meningkatkan karakter ASCII dari surat terakhir, dan melakukan BETWEEN. Anda kemudian mendapatkan kinerja terbaik, untuk subset dari fungsi LIKE. Tentu saja, ini hanya berfungsi dalam situasi tertentu, tetapi untuk kumpulan data ultra-besar di mana Anda mencari nama misalnya, itu membuat kinerja berubah dari buruk menjadi dapat diterima.
sumber
Pertanyaan yang sangat lama, tetapi saya menemukan solusi cepat untuk masalah ini:
Karena fungsi ascii () hanya terlihat pada karakter pertama dari string.
sumber
(name)
?Untuk memeriksa inisial, saya sering menggunakan casting untuk
"char"
(dengan tanda kutip ganda). Ini tidak portabel, tetapi sangat cepat. Secara internal, itu hanya detoasts teks dan mengembalikan karakter pertama, dan operasi perbandingan "char" sangat cepat karena jenisnya adalah 1 byte panjang tetap:Perhatikan bahwa casting ke
"char"
lebih cepat daripadaascii()
slution oleh @ Sole021, tetapi itu tidak kompatibel dengan UTF8 (atau pengkodean lainnya dalam hal ini), hanya mengembalikan byte pertama, jadi sebaiknya hanya digunakan dalam kasus-kasus di mana perbandingannya terhadap plain old 7 -bit ASCII karakter.sumber
Ada dua metode yang belum disebutkan untuk menangani kasus-kasus tersebut:
sebagian (atau dipartisi - jika dibuat untuk rentang penuh secara manual) indeks - paling berguna ketika hanya sebagian dari data yang diperlukan (misalnya selama beberapa pemeliharaan atau sementara untuk beberapa pelaporan):
mempartisi tabel itu sendiri (menggunakan karakter pertama sebagai kunci pemartisian) - teknik ini sangat layak dipertimbangkan dalam PostgreSQL 10+ (partisi yang tidak terlalu menyakitkan) dan 11+ (pemangkasan partisi saat eksekusi query).
Selain itu, jika data dalam tabel diurutkan, orang bisa mendapat manfaat dari menggunakan indeks BRIN (lebih dari karakter pertama).
sumber
Mungkin lebih cepat untuk melakukan perbandingan satu karakter:
sumber
column LIKE 'B%'
akan lebih efisien daripada menggunakan fungsi substring pada kolom.