PostgreSQL: Cara membuat kueri "case-insensitive"


339

Apakah ada cara untuk menulis query case-insensitive di PostgreSQL, Mis. Saya ingin 3 query berikut mengembalikan hasil yang sama.

SELECT id FROM groups where name='administrator'

SELECT id FROM groups where name='ADMINISTRATOR'

SELECT id FROM groups where name='Administrator'

jika citext datang dengan instalasi Postgres Anda, coba ketik citext. Ini adalah case-insensitive text
Michael Buen

2
Untuk pendatang baru untuk pertanyaan ini, tautan ini ke dokumentasi postgres resmi berisi semua jawaban yang diberikan di sini, serta beberapa opsi lainnya.
Parthian Shot

Pak, tetapkan kembali jawaban yang diterima untuk jawaban yang dibuat oleh @Arun. Ini jauh lebih rumit dan tidak menarik banyak masalah setelah mendaftar.
zeliboba

Jawaban:


451

Gunakan fungsi RENDAH untuk mengkonversi string ke huruf kecil sebelum membandingkan.

Coba ini:

SELECT id 
  FROM groups
 WHERE LOWER(name)=LOWER('Administrator')

92
Penting untuk dicatat bahwa menggunakan RENDAH (atau fungsi apa pun) pada kolom predikat - dalam hal ini "nama" - akan menyebabkan indeks tidak lagi dapat dicari. Jika ini adalah tabel besar atau sering ditanyakan, itu bisa menyebabkan masalah. Penyusunan case-insensitive, citext, atau indeks berbasis fungsi akan meningkatkan kinerja.
Jordan

108
Atau cukup buat indeks seperti ini: CREATE INDEX idx_groups_name ON groups lower (nama);
Daniel

19
Tentukan juga varchar_pattern_opsjika Anda ingin indeks berfungsi dengan LIKE 'xxx%'kueri, yaitu CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops).
sayap

10
Menggunakan operator ILIKE (seperti yang ditunjukkan dalam jawaban lain di bawah) adalah pendekatan yang lebih sederhana, meskipun ini adalah jawaban yang paling banyak dipilih.
Ryan

5
Melalui komentar di sini, banyak saran di sini menyarankan ILIKE, Ini akan berhasil but with slow response,. Untuk mendapatkan akses cepat ke tabel berdasarkan hasil perhitungan, saya sarankan siapa pun yang hanya memeriksa ini harus pergi dengan jawaban yang diterima. Lihat detail lebih lanjut di sini dan di sini
Afolabi Olaoluwa Akinwumi

231

menggunakan ILIKEbukanLIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'

1
Catatan yang ILIKEtidak didukung oleh Hibernate saat digunakan di Spring Boot.
AnT

@AnT berfungsi dengan org.hibernate.dialect.PostgreSQL94Dialectdan Spring Boot 2.0.6.RELEASE. Tapi IntelliJ mengeluhkan hal itu.
Samintha Kaveesh

134

Pendekatan yang paling umum adalah untuk huruf kecil atau huruf besar string pencarian dan data. Tapi ada dua masalah dengan itu.

  1. Ini bekerja dalam bahasa Inggris, tetapi tidak dalam semua bahasa. (Mungkin bahkan tidak di sebagian besar bahasa.) Tidak setiap huruf kecil memiliki huruf besar yang sesuai; tidak setiap huruf besar memiliki huruf kecil yang sesuai.
  2. Menggunakan fungsi seperti lower () dan upper () akan memberi Anda pemindaian berurutan. Itu tidak bisa menggunakan indeks. Pada sistem pengujian saya, menggunakan lebih rendah () memakan waktu sekitar 2000 kali lebih lama daripada kueri yang dapat menggunakan indeks. (Data uji memiliki sedikit lebih dari 100 ribu baris.)

Setidaknya ada tiga solusi yang jarang digunakan yang mungkin lebih efektif.

  1. Gunakan modul citext , yang sebagian besar meniru perilaku tipe data case-insensitive. Setelah memuat modul itu, Anda dapat membuat indeks case-insensitive oleh CREATE INDEX ON groups (name::citext);. (Tapi lihat di bawah.)
  2. Gunakan susunan case-insensitive. Ini diatur ketika Anda menginisialisasi database. Menggunakan susunan case-insensitive berarti Anda dapat menerima hampir semua format dari kode klien, dan Anda masih akan memberikan hasil yang bermanfaat. (Ini juga berarti Anda tidak dapat melakukan permintaan huruf besar-kecil. Duh.)
  3. Buat indeks fungsional. Buat indeks huruf kecil dengan menggunakan CREATE INDEX ON groups (LOWER(name));. Setelah melakukannya, Anda dapat mengambil keuntungan dari indeks dengan pertanyaan seperti SELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');, atau SELECT id FROM groups WHERE LOWER(name) = 'administrator';Anda harus ingat untuk menggunakan RENDAH ().

Modul citext tidak memberikan tipe data case-insensitive yang sebenarnya. Sebaliknya, ia berperilaku seolah-olah setiap string lebih kecil. Artinya, berperilaku seolah-olah Anda telah memanggil lower()setiap string, seperti pada angka 3 di atas. Keuntungannya adalah bahwa programmer tidak perlu mengingat string huruf kecil. Tetapi Anda perlu membaca bagian "Perilaku Perbandingan String" dan "Keterbatasan" dalam dokumen sebelum Anda memutuskan untuk menggunakan citext.


1
Tentang # 1: Seharusnya tidak menjadi masalah, karena itu akan menjadi dua string yang berbeda (anggap suka melakukan col = 'a'dan col = 'b'). Tentang # 2: Seperti yang Anda katakan, Anda bisa membuat indeks pada ekspresi, jadi itu bukan masalah. Tapi saya setuju dengan Anda bahwa mengubah susunan adalah solusi terbaik.
Vincent Savard

5
Dapatkah seseorang memberi tahu saya apa susunan tidak peka huruf besar yang merupakan susunan bawaan PostgreSQL? Saya melihat ini sebagai opsi tetapi tidak dapat menemukan apa pun tentang susunan case-insensitive untuk Postgres di internet?
khorvat

1
@AnupShah: Tidak, saya tidak mengatakan itu. Saya tidak menjalankan PostgreSQL di Windows. 9,4 dokumen mengatakan ini : "Pada semua platform, tersedia collations bernama default, C, dan POSIX. Collations tambahan mungkin tersedia tergantung pada dukungan sistem operasi." Anda dapat melihat koleksi mana yang menurut PostgreSQL tersedia select * from pg_collation;.
Mike Sherrill 'Cat Recall'

1
@ Matthieu: Ini adalah pengantar terbaik (dan hati-hati) untuk subjek yang saya tahu tentang: Kasus Tepi yang Perlu Diingat. Bagian 1 - Teks .
Mike Sherrill 'Cat Recall'


95

Anda bisa menggunakannya ILIKE. yaitu

SELECT id FROM groups where name ILIKE 'administrator'

Benar dan berfungsi dengan baik untuk saya, saya menggunakan MAC OS X (Mountain Lion).
ADJ

5
Ini akan berhasil, tetapi dengan respons yang lambat. Untuk mendapatkan akses cepat ke tabel berdasarkan hasil perhitungan, saya sarankan untuk menggunakan lowerfungsi ini. Lihat lebih detail
Afolabi Olaoluwa Akinwumi

1
@AfolabiOlaoluwaAkinwumi pada dasarnya ini tergantung pada apakah Anda mencari hasil yang bertentangan dengan penyaringan nilai yang diketahui . Dalam kasus terakhir, kasus seragam tunggal harus dipertahankan pada tingkat data yang memungkinkan operator kesetaraan bekerja. [Rekomendasi pribadi adalah huruf besar pascal untuk nilai kode jenis]
Chris Marisic

53

Anda juga dapat membaca ILIKEkata kunci. Kadang-kadang bisa sangat berguna, meskipun tidak sesuai dengan standar SQL. Lihat di sini untuk informasi lebih lanjut: http://www.postgresql.org/docs/9.2/static/functions-matching.html


9
Sesuatu yang harus diperhatikan di sini adalah input pengguna jahat. Jika Anda menjalankan kueri suka email ILIKE 'user-input-email-here', pastikan untuk keluar dari input pengguna. Kalau tidak, orang dapat memasukkan karakter seperti% yang cocok dengan apa pun.
Matt De Leon

2
@MattDeLeon Hai. Kata baik. Tetapi saya hanya ingin bertanya kepada Anda, apakah saya menggunakan ILIKEdan prepared statementsapakah ini akan melindungi saya dari sql injection?
slevin

Tidak yakin, saya kira Anda ingin mengirim string pelarian ke pernyataan yang disiapkan.
Matt De Leon

1
"Kata kunci ILIKE dapat digunakan alih-alih LIKE untuk membuat kecocokan huruf besar-kecil sesuai dengan lokal aktif. Ini bukan dalam standar SQL tetapi merupakan ekstensi PostgreSQL." Bekerja seperti pesona di 9.3
Aleksey Deryagin

1
ILIKE lebih lambat dari lower(column_name) like %expression%.
Patryk Imosa

28

Anda juga bisa menggunakan ekspresi reguler POSIX, seperti

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' kembali t


1
Saya memiliki masalah yang sama, saya perlu pencarian case sensitif pada database PostgreSQL saya. Saya berpikir tentang mengubah string input pengguna menjadi ekspresi reguler. Sekarang, menggunakan ~ * bukannya = atau LIKE bekerja dengan sempurna! Saya tidak perlu membuat indeks baru, kolom atau apa pun. Tentu, pencarian regex lebih lambat daripada perbandingan byte langsung, tetapi saya tidak berpikir dampak pada kinerja akan jauh lebih besar daripada harus menangani dua set data (satu lebih rendah atau lebih hanya untuk mencari, kemudian harus mengambil yang asli sesuai data dari set lainnya). Selain itu, ini lebih bersih!
Cyberknight

1
Baik, tetapi bagaimana hubungannya dengan regexp_matches () misalnya?
WKT

Menurut postgres docs: Operator ~~ setara dengan LIKE, dan ~~ * sesuai dengan ILIKE. Ada juga operator ~~ dan! ~~ * yang masing-masing mewakili TIDAK SEPERTI BUKAN ILIKE. Semua operator ini khusus untuk PostgreSQL.
sh4

Saya menghadapi masalah ketika tanda kurung dimasukkan dalam teks, itu tidak berfungsi. seperti: "code (LC)"
Oshan Wisumperuma

8

Penggunaan ~*dapat sangat meningkatkan kinerja, dengan fungsionalitas INSTR.

SELECT id FROM groups WHERE name ~* 'adm'

kembalikan baris dengan nama yang mengandung ATAU sama dengan 'adm'.


1
Hei, Robin, selamat datang di SO. Jawaban James Brown sudah mengusulkan solusi ini. Selain itu, jawaban yang Anda usulkan tidak memanfaatkan regex dengan cara apa pun.
Rafael
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.