Pencarian case sensitif di Oracle

228

Perilaku default LIKEdan operator pembanding lainnya, =dll peka huruf besar-kecil.

Apakah mungkin membuat mereka tidak peka terhadap huruf besar-kecil?

sergionni
sumber
Pengingat ramah bahwa beberapa contoh pencarian akan menghasilkan pemindaian tabel penuh bahkan jika ada indeks pada user_name.
JonSG
8
Sudahkah Anda mempertimbangkan untuk menggunakan REGEXP_LIKE(username,'me','i')bukannya LIKE?
kubanczyk
5
tidak, LIKE berfungsi baik untuk saya
sergionni

Jawaban:

82

Sejak 10gR2, Oracle memungkinkan untuk menyempurnakan perilaku perbandingan string dengan mengatur parameter NLS_COMPdan NLS_SORTsesi:

SQL> SET HEADING OFF
SQL> SELECT *
  2  FROM NLS_SESSION_PARAMETERS
  3  WHERE PARAMETER IN ('NLS_COMP', 'NLS_SORT');

NLS_SORT
BINARY

NLS_COMP
BINARY


SQL>
SQL> SELECT CASE WHEN 'abc'='ABC' THEN 1 ELSE 0 END AS GOT_MATCH
  2  FROM DUAL;

         0

SQL>
SQL> ALTER SESSION SET NLS_COMP=LINGUISTIC;

Session altered.

SQL> ALTER SESSION SET NLS_SORT=BINARY_CI;

Session altered.

SQL>
SQL> SELECT *
  2  FROM NLS_SESSION_PARAMETERS
  3  WHERE PARAMETER IN ('NLS_COMP', 'NLS_SORT');

NLS_SORT
BINARY_CI

NLS_COMP
LINGUISTIC


SQL>
SQL> SELECT CASE WHEN 'abc'='ABC' THEN 1 ELSE 0 END AS GOT_MATCH
  2  FROM DUAL;

         1

Anda juga dapat membuat indeks case sensitive:

create index
   nlsci1_gen_person
on
   MY_PERSON
   (NLSSORT
      (PERSON_LAST_NAME, 'NLS_SORT=BINARY_CI')
   )
;

Informasi ini diambil dari pencarian tidak sensitif case Oracle . Artikel itu menyebutkan REGEXP_LIKEtetapi tampaknya bekerja dengan baik =juga.


Dalam versi yang lebih tua dari 10gR2 itu tidak dapat benar-benar dilakukan dan pendekatan yang biasa, jika Anda tidak memerlukan pencarian tidak peka-aksen , adalah hanya UPPER()kolom dan ekspresi pencarian.

Álvaro González
sumber
1
Ini bekerja dengan baik, tetapi itu membuat UPDATES menggunakan operator LIKE / = sangat lambat ...... :(
Saqib Ali
1
@SaqibAli LIKEEkspresi sewenang-wenang (misalnya WHERE foo LIKE '%abc%') sudah cukup lambat jika mereka tidak dapat diindeks, saya tidak berpikir itu terkait dengan sensitivitas huruf.
Álvaro González
1
Anda juga dapat mengatur ini di luar SQLPLUS, seperti di lingkungan shell. Misalnya dalam skrip Perl menggunakan DBD::Oracle, Anda dapat menulis $ENV{NLS_SORT} = 'BINARY_CI'; $ENV{NLS_COMP} = 'LINGUISTIC';sebelum memanggil `DBI-> connect`.
mivk
hei apakah ALTER SESSIONhanya mengubah instance lokal Anda dari koreksi dan apakah itu berarti seperti sesi Anda saat ini yaitu jika saya menutup dan membuka kembali itu akan reset. Apakah ada cara agar saya dapat melihat apa nilai saat ini sehingga jika tetap ada di mana-mana saya dapat kembali ke pengaturan semula ...
Seabizkit
305

Ada 3 cara utama untuk melakukan pencarian case-insensitive di Oracle tanpa menggunakan indeks teks lengkap.

Pada akhirnya, metode apa yang Anda pilih bergantung pada keadaan pribadi Anda; hal utama yang harus diingat adalah untuk meningkatkan kinerja Anda harus mengindeks dengan benar untuk pencarian case-sensitive.

1. Hurufkan kolom dan string Anda secara identik.

Anda dapat memaksa semua data Anda menjadi kasus yang sama dengan menggunakan UPPER()atau LOWER():

select * from my_table where upper(column_1) = upper('my_string');

atau

select * from my_table where lower(column_1) = lower('my_string');

Jika column_1tidak diindeks pada upper(column_1)atau lower(column_1), jika sesuai, ini mungkin memaksa pemindaian tabel penuh. Untuk menghindari ini, Anda dapat membuat indeks berbasis fungsi .

create index my_index on my_table ( lower(column_1) );

Jika Anda menggunakan LIKE maka Anda harus menyatukan %sekitar string yang Anda cari.

select * from my_table where lower(column_1) LIKE lower('my_string') || '%';

SQL Fiddle ini menunjukkan apa yang terjadi di semua kueri ini. Perhatikan Rencana Jelaskan, yang menunjukkan kapan indeks digunakan dan kapan tidak.

2. Gunakan ekspresi reguler.

Dari Oracle 10g dan seterusnya REGEXP_LIKE()tersedia. Anda dapat menentukan _match_parameter_ 'i', untuk melakukan pencarian case-insensitive.

Untuk menggunakan ini sebagai operator kesetaraan, Anda harus menentukan awal dan akhir string, yang dilambangkan dengan karat dan tanda dolar.

select * from my_table where regexp_like(column_1, '^my_string$', 'i');

Untuk melakukan yang setara dengan LIKE, ini dapat dihapus.

select * from my_table where regexp_like(column_1, 'my_string', 'i');

Hati-hati dengan ini karena string Anda mungkin berisi karakter yang akan ditafsirkan berbeda oleh mesin ekspresi reguler.

SQL Fiddle ini menunjukkan kepada Anda contoh output yang sama kecuali menggunakan REGEXP_LIKE ().

3. Ubah di tingkat sesi.

The NLS_SORT parameter mengatur urutan pemeriksaan untuk pemesanan dan berbagai operator perbandingan, termasuk =dan SEBAGAINYA. Anda bisa menentukan jenis biner, tidak peka huruf besar-kecil, dengan mengubah sesi. Ini berarti bahwa setiap kueri yang dilakukan dalam sesi itu akan melakukan parameter case-insensitive.

alter session set nls_sort=BINARY_CI

Ada banyak informasi tambahan seputar penyortiran linguistik dan pencarian string jika Anda ingin menentukan bahasa yang berbeda, atau melakukan pencarian yang peka terhadap aksen menggunakan BINARY_AI.

Anda juga perlu mengubah parameter NLS_COMP ; kutipan:

Operator yang tepat dan klausa permintaan yang mematuhi parameter NLS_SORT tergantung pada nilai parameter NLS_COMP. Jika operator atau klausa tidak mematuhi nilai NLS_SORT, seperti yang ditentukan oleh NLS_COMP, susunan yang digunakan adalah BINARY.

Nilai default NLS_COMP adalah BINARY; tetapi, LINGUISTIC menetapkan bahwa Oracle harus memperhatikan nilai NLS_SORT:

Perbandingan untuk semua operasi SQL di klausa WHERE dan di PL / SQL blok harus menggunakan jenis linguistik yang ditentukan dalam parameter NLS_SORT. Untuk meningkatkan kinerja, Anda juga dapat menentukan indeks linguistik pada kolom yang Anda inginkan perbandingan linguistik.

Jadi, sekali lagi, Anda perlu mengubah sesi

alter session set nls_comp=LINGUISTIC

Sebagaimana dicatat dalam dokumentasi Anda mungkin ingin membuat indeks linguistik untuk meningkatkan kinerja

create index my_linguistc_index on my_table 
   (NLSSORT(column_1, 'NLS_SORT = BINARY_CI'));
Ben
sumber
"buat indeks berbasis fungsi" Luar biasa apa perbedaan ini dapat membuat
Jacob Goulden
Bolehkah saya bertanya mengapa berbeda select * from my_table where lower(column_1) LIKE lower('my_string') || '%';daripada melakukan select * from my_table where lower(column_1) LIKE lower('my_string%');? Apakah ada manfaatnya?
lopezvit
1
Salah satu alasannya adalah jika permintaan Anda diparamerisasi (kemungkinan dalam kebanyakan situasi) maka kode panggilan Anda tidak perlu selalu menyatukan% di akhir @lopezvit.
Ben
1
Jika ada beberapa karakter yang akan mengacaukan hasil regexp_like, apakah ada cara untuk melarikan diri dari string seperti itu? Memberi contoh, jika string memiliki $, output tidak akan seperti yang kita harapkan. // cc @Ben dan yang lainnya tolong lakukan berbagi.
bozzmob
2
` adalah karakter escape @bozzmob. Seharusnya tidak ada perbedaan dalam output jika string ekspresi reguler beroperasi berisi $, ini hanya dapat menyebabkan masalah jika Anda memerlukan $literal dalam ekspresi reguler Anda. Jika Anda memiliki masalah tertentu, saya akan mengajukan pertanyaan lain jika komentar / jawaban ini tidak membantu.
Ben
51

mungkin Anda bisa mencoba menggunakan

SELECT user_name
FROM user_master
WHERE upper(user_name) LIKE '%ME%'
V4Vendetta
sumber
3
ini bekerja ketika parameter input keseluruhan huruf besar, dan jika lebih rendah atau campuran tidak
sergionni
13
Sudahkah Anda memikirkannya WHERE upper(user_name) LIKE UPPER('%ME%')? :)
Konerak
3
@sergionni Anda juga harus menggunakan huruf besar untuk istilah pencarian!
Markus Winand
3
@sergionni, lalu mengapa Anda tidak menggunakan UPPERparameter input juga?
Czechnology
5
@ V4Vendetta menggunakan upperfungsi Anda kehilangan indeks, apakah Anda tahu cara melakukan pencarian menggunakan indeks?
jcho360
7

Dari Oracle 12c R2 Anda dapat menggunakan COLLATE operator:

Operator COLLATE menentukan collation untuk ekspresi. Operator ini memungkinkan Anda untuk mengganti susunan yang akan dihasilkan oleh basis data untuk ekspresi menggunakan aturan derivasi susunan standar.

Operator COLLATE mengambil satu argumen, collation_name, yang Anda dapat menentukan collation bernama atau pseudo-collation. Jika nama collation berisi spasi, maka Anda harus menyertakan nama dalam tanda kutip ganda.

Demo:

CREATE TABLE tab1(i INT PRIMARY KEY, name VARCHAR2(100));

INSERT INTO tab1(i, name) VALUES (1, 'John');
INSERT INTO tab1(i, name) VALUES (2, 'Joe');
INSERT INTO tab1(i, name) VALUES (3, 'Billy'); 
--========================================================================--
SELECT /*csv*/ *
FROM tab1
WHERE name = 'jOHN' ;
-- no rows selected

SELECT /*csv*/ *
FROM tab1
WHERE name COLLATE BINARY_CI = 'jOHN' ;
/*
"I","NAME"
1,"John"
*/

SELECT /*csv*/ *
FROM tab1 
WHERE name LIKE 'j%';
-- no rows selected

SELECT /*csv*/ *
FROM tab1 
WHERE name COLLATE BINARY_CI LIKE 'j%';
/*
"I","NAME"
1,"John"
2,"Joe"
*/

db <> demo biola

Lukasz Szozda
sumber
2
select user_name
from my_table
where nlssort(user_name, 'NLS_SORT = Latin_CI') = nlssort('%AbC%', 'NLS_SORT = Latin_CI')
Clodoaldo Neto
sumber
The %'s dalam argumen pertama untuk kedua Anda NLSSORTyang tidak dimaksudkan untuk menjadi wildcard, kan? Mereka agak bingung.
Stefan van den Akker
1

Anda dapat melakukan sesuatu seperti itu:

where regexp_like(name, 'string$', 'i');
grep
sumber