Trường hợp tìm kiếm không nhạy cảm trong Oracle


228

Hành vi mặc định của LIKEvà các toán tử so sánh khác, =vv là phân biệt chữ hoa chữ thường.

Có thể làm cho họ không nhạy cảm trường hợp?


Nhắc nhở thân thiện rằng một số tìm kiếm mẫu sẽ dẫn đến quét toàn bộ bảng ngay cả khi có chỉ mục trên user_name.
JonSG

8
Bạn đã cân nhắc sử dụng REGEXP_LIKE(username,'me','i')thay vì THÍCH?
kubanchot

5
không, THÍCH hoạt động tốt với tôi
sergionni

Câu trả lời:


82

Kể từ 10gR2, Oracle cho phép tinh chỉnh hành vi so sánh chuỗi bằng cách đặt tham số NLS_COMPNLS_SORTphiên:

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

Bạn cũng có thể tạo các chỉ mục không phân biệt chữ hoa chữ thường:

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

Thông tin này được lấy từ các trường hợp tìm kiếm không nhạy cảm của Oracle . Bài báo đề cập REGEXP_LIKEnhưng nó dường như cũng làm việc với tốt cũ =.


Trong các phiên bản cũ hơn 10gR2, điều đó thực sự không thể thực hiện được và cách tiếp cận thông thường, nếu bạn không cần tìm kiếm không có dấu , chỉ là UPPER()cả cột và biểu thức tìm kiếm.


1
Điều này hoạt động tốt, nhưng nó làm cho các CẬP NHẬT sử dụng các toán tử THÍCH / = rất chậm ...... :(
Saqib Ali

1
@SaqibAli Các LIKEbiểu thức tùy ý (ví dụ WHERE foo LIKE '%abc%') đã đủ chậm nếu chúng không thể được lập chỉ mục, tôi không nghĩ nó liên quan cụ thể đến tính nhạy cảm của trường hợp.
Álvaro González

1
Bạn cũng có thể đặt chúng bên ngoài SQLPLUS, như trong môi trường shell. Ví dụ: trong tập lệnh Perl bằng cách sử dụng DBD::Oracle, bạn có thể viết $ENV{NLS_SORT} = 'BINARY_CI'; $ENV{NLS_COMP} = 'LINGUISTIC';trước khi gọi `DBI-> connect`.
mivk

hey ALTER SESSIONchỉ thay đổi phiên bản địa phương của bạn về sửa lỗi và nó có nghĩa như phiên hiện tại của bạn tức là nếu tôi đóng và mở lại thì nó sẽ được đặt lại. Có cách nào để tôi có thể thấy các giá trị hiện tại là gì để nếu nó tồn tại ở mọi nơi tôi có thể thay đổi lại cài đặt gốc ...
Seabizkit

305

Có 3 cách chính để thực hiện tìm kiếm không phân biệt chữ hoa chữ thường trong Oracle mà không cần sử dụng các chỉ mục toàn văn.

Cuối cùng, phương pháp bạn chọn phụ thuộc vào hoàn cảnh cá nhân của bạn; điều chính cần nhớ là để cải thiện hiệu suất, bạn phải lập chỉ mục chính xác cho tìm kiếm không phân biệt chữ hoa chữ thường.

1. Trường hợp cột của bạn và chuỗi của bạn giống hệt nhau.

Bạn có thể buộc tất cả dữ liệu của mình giống nhau bằng cách sử dụng UPPER()hoặc LOWER():

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

hoặc là

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

Nếu column_1không được lập chỉ mục trên upper(column_1)hoặc lower(column_1), nếu phù hợp, điều này có thể buộc quét toàn bộ bảng. Để tránh điều này, bạn có thể tạo một chỉ mục dựa trên chức năng .

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

Nếu bạn đang sử dụng THÍCH thì bạn phải nối một %chuỗi xung quanh chuỗi bạn đang tìm kiếm.

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

Fiddle SQL này cho thấy những gì xảy ra trong tất cả các truy vấn này. Lưu ý Kế hoạch Giải thích, cho biết khi nào một chỉ mục đang được sử dụng và khi nào thì không.

2. Sử dụng các biểu thức thông thường.

Từ Oracle 10g trở đi REGEXP_LIKE()có sẵn. Bạn có thể chỉ định _match_parameter_ 'i', để thực hiện tìm kiếm không phân biệt chữ hoa chữ thường.

Để sử dụng điều này như một toán tử đẳng thức, bạn phải chỉ định điểm bắt đầu và kết thúc của chuỗi, được biểu thị bằng carat và ký hiệu đô la.

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

Để thực hiện tương đương với THÍCH, chúng có thể được gỡ bỏ.

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

Hãy cẩn thận với điều này vì chuỗi của bạn có thể chứa các ký tự sẽ được diễn giải khác nhau bởi công cụ biểu thức chính quy.

Fiddle SQL này cho bạn thấy đầu ra ví dụ tương tự ngoại trừ sử dụng REGEXP_LIKE ().

3. Thay đổi nó ở cấp phiên.

Các NLS_SORT tham số điều chỉnh chuỗi collation cho đặt hàng và các toán tử so sánh khác nhau, bao gồm =và LIKE. Bạn có thể chỉ định nhị phân, không phân biệt chữ hoa chữ thường, sắp xếp bằng cách thay đổi phiên. Điều này có nghĩa là mọi truy vấn được thực hiện trong phiên đó sẽ thực hiện các tham số không phân biệt chữ hoa chữ thường.

alter session set nls_sort=BINARY_CI

Có rất nhiều thông tin bổ sung xung quanh việc sắp xếp ngôn ngữ và tìm kiếm chuỗi nếu bạn muốn chỉ định một ngôn ngữ khác hoặc thực hiện tìm kiếm không nhạy cảm bằng cách sử dụng BINARY_AI.

Bạn cũng sẽ cần thay đổi tham số NLS_COMP ; để trích:

Các toán tử và mệnh đề truy vấn chính xác tuân theo tham số NLS_SORT phụ thuộc vào giá trị của tham số NLS_COMP. Nếu một toán tử hoặc mệnh đề không tuân theo giá trị NLS_SORT, như được xác định bởi NLS_COMP, thì đối chiếu được sử dụng là BINary.

Giá trị mặc định của NLS_COMP là BINary; nhưng, LINGUISTIC chỉ định rằng Oracle nên chú ý đến giá trị của NLS_SORT:

So sánh tất cả các hoạt động SQL trong mệnh đề WHERE và trong các khối PL / SQL nên sử dụng sắp xếp ngôn ngữ được chỉ định trong tham số NLS_SORT. Để cải thiện hiệu suất, bạn cũng có thể xác định một chỉ số ngôn ngữ trên cột mà bạn muốn so sánh ngôn ngữ.

Vì vậy, một lần nữa, bạn cần thay đổi phiên

alter session set nls_comp=LINGUISTIC

Như đã lưu ý trong tài liệu, bạn có thể muốn tạo một chỉ mục ngôn ngữ để cải thiện hiệu suất

create index my_linguistc_index on my_table 
   (NLSSORT(column_1, 'NLS_SORT = BINARY_CI'));

"Tạo một chỉ mục dựa trên chức năng" Thật đáng ngạc nhiên về sự khác biệt mà điều này có thể tạo ra
Jacob Goulden

Tôi có thể hỏi tại sao nó khác để làm select * from my_table where lower(column_1) LIKE lower('my_string') || '%';thay vì select * from my_table where lower(column_1) LIKE lower('my_string%');? Liệu nó có mang lại lợi thế nào không?
lopezvit

1
Một lý do sẽ là nếu truy vấn của bạn được tối ưu hóa (có thể trong hầu hết các tình huống) thì mã cuộc gọi của bạn không cần phải luôn nối một% ở cuối @lopezvit.
Ben

1
Nếu có một số nhân vật sẽ gây rối kết quả regexp_like, liệu có cách nào để thoát khỏi chuỗi đó không? Đưa ra một ví dụ, nếu chuỗi có $, đầu ra sẽ không như những gì chúng ta mong đợi. // cc @Ben và những người khác xin vui lòng chia sẻ.
bozzmob

2
` là nhân vật thoát @bozzmob. Không có sự khác biệt về đầu ra nếu chuỗi biểu thức chính quy đang hoạt động có chứa a $, điều này chỉ có thể gây ra sự cố cho bạn nếu bạn cần một $chữ trong biểu thức chính quy. Nếu bạn có một vấn đề cụ thể, tôi sẽ hỏi một câu hỏi khác nếu nhận xét / câu trả lời này không có ích.
Ben

51

có lẽ bạn có thể thử sử dụng

SELECT user_name
FROM user_master
WHERE upper(user_name) LIKE '%ME%'

3
nó hoạt động khi tham số đầu vào là toàn bộ chữ hoa và nếu thấp hơn hoặc hỗn hợp thì không
sergionni 22/03/2016

13
Bạn đã nghĩ về WHERE upper(user_name) LIKE UPPER('%ME%')sau đó? :)
Konerak

3
@sergionni bạn cũng phải viết hoa cụm từ tìm kiếm!
Markus Winand

3
@sergionni, vậy thì tại sao bạn không sử dụng UPPERtrên tham số đầu vào?
Công nghệ

5
@ V4Vendetta sử dụng upperchức năng bạn làm mất chỉ mục, bạn có biết làm thế nào để tìm kiếm bằng chỉ mục không?
jcho360

7

Từ Oracle 12c R2 bạn có thể sử dụng COLLATE operator:

Toán tử COLLATE xác định đối chiếu cho một biểu thức. Toán tử này cho phép bạn ghi đè lên đối chiếu mà cơ sở dữ liệu sẽ có được cho biểu thức bằng cách sử dụng các quy tắc dẫn xuất đối chiếu chuẩn.

Toán tử COLLATE lấy một đối số, collation_name, trong đó bạn có thể chỉ định đối chiếu có tên hoặc đối chiếu giả. Nếu tên đối chiếu chứa một khoảng trắng, thì bạn phải đặt tên đó trong dấu ngoặc kép.

Bản giới thiệu:

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 <> fiddle demo


2
select user_name
from my_table
where nlssort(user_name, 'NLS_SORT = Latin_CI') = nlssort('%AbC%', 'NLS_SORT = Latin_CI')

Các %'s trong đối số đầu tiên thứ hai của bạn NLSSORTđang không có nghĩa là ký tự đại diện, phải không? Họ nhầm lẫn.
Stefan van den Akker

1

bạn có thể làm một cái gì đó như thế:

where regexp_like(name, 'string$', 'i');
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.