การค้นหาแบบคำนึงถึงขนาดตัวพิมพ์ใน Oracle


228

พฤติกรรมเริ่มต้นของLIKEและผู้ประกอบการเปรียบเทียบ=อื่น ๆ เป็นกรณี ๆ ไป

เป็นไปได้หรือไม่ที่จะทำให้มันเป็นตัวพิมพ์เล็ก


การเตือนที่เป็นมิตรที่ตัวอย่างการค้นหาบางอย่างจะส่งผลให้การสแกนตารางเต็มแม้ว่าจะมีดัชนีในชื่อผู้ใช้
JonSG

8
คุณคิดว่าจะใช้REGEXP_LIKE(username,'me','i')แทน LIKE หรือไม่?
kubanczyk

5
ไม่ LIKE ใช้งานได้ดีสำหรับฉัน
sergionni

คำตอบ:


82

ตั้งแต่ 10gR2, Oracle อนุญาตให้ปรับพฤติกรรมการเปรียบเทียบสตริงโดยการตั้งค่าNLS_COMPและNLS_SORTพารามิเตอร์เซสชั่น:

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

คุณยังสามารถสร้างดัชนีที่ไม่สนใจตัวพิมพ์เล็กและตัวพิมพ์ใหญ่:

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

ข้อมูลนี้จะถูกนำมาจากออราเคิลค้นหากรณีตาย บทความกล่าวถึงREGEXP_LIKEแต่ดูเหมือนว่าจะทำงานกับเก่าดี=เช่นกัน


ในรุ่นที่เก่ากว่า 10gR2 มันไม่สามารถทำได้จริง ๆ และวิธีการทั่วไปถ้าคุณไม่ต้องการการค้นหาที่เน้นเสียงไม่รู้สึกก็คือการใช้UPPER()ทั้งคอลัมน์และนิพจน์การค้นหา


1
วิธีนี้ใช้งานได้ดี แต่มันทำให้การอัพเดทโดยใช้ตัวดำเนินการ LIKE / = ช้ามาก ...... :(
Saqib Ali

1
@SaqibAli LIKEการแสดงออกโดยพลการ (เช่นWHERE foo LIKE '%abc%') นั้นช้าพอหากไม่สามารถจัดทำดัชนีได้ฉันไม่คิดว่ามันเกี่ยวข้องกับความไวของ case โดยเฉพาะ
ÁlvaroGonzález

1
คุณสามารถตั้งค่าเหล่านี้นอก SQLPLUS เช่นในสภาพแวดล้อมของเชลล์ ตัวอย่างเช่นในสคริปต์ Perl โดยใช้DBD::Oracleคุณสามารถเขียน$ENV{NLS_SORT} = 'BINARY_CI'; $ENV{NLS_COMP} = 'LINGUISTIC';ก่อนที่จะเรียก "DBI-> connect"
mivk

เฮ้จะALTER SESSIONแก้ไขอินสแตนซ์ในการแก้ไขเท่านั้นและมันหมายถึงเซสชั่นปัจจุบันของคุณคือถ้าฉันปิดและเปิดใหม่มันจะรีเซ็ต มีวิธีที่ฉันสามารถดูว่าค่าปัจจุบันคืออะไรเพื่อให้มันคงอยู่ทุกหนทุกแห่งฉันสามารถเปลี่ยนกลับเป็นการตั้งค่าดั้งเดิม ...
Seabizkit

305

มี 3 วิธีหลักในการค้นหาแบบตรงตามตัวพิมพ์ใน Oracle โดยไม่ต้องใช้ดัชนีข้อความแบบเต็ม

ในที่สุดวิธีการที่คุณเลือกนั้นขึ้นอยู่กับสถานการณ์ของคุณ สิ่งสำคัญที่ต้องจำคือการปรับปรุงประสิทธิภาพคุณต้องจัดทำดัชนีอย่างถูกต้องสำหรับการค้นหาแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่

1. กรณีคอลัมน์และสตริงของคุณเหมือนกัน

คุณสามารถบังคับให้ข้อมูลทั้งหมดของคุณเป็นกรณีเดียวกันโดยใช้UPPER()หรือLOWER():

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

หรือ

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

หากcolumn_1ไม่ได้จัดทำดัชนีupper(column_1)หรือlower(column_1)ตามความเหมาะสมอาจเป็นการสแกนแบบเต็มตาราง เพื่อหลีกเลี่ยงการนี้คุณสามารถสร้างดัชนีฟังก์ชั่นที่ใช้

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

หากคุณใช้ LIKE คุณจะต้องเชื่อม%สตริงที่คุณกำลังค้นหา

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

ซอ Fiddle นี้แสดงให้เห็นถึงสิ่งที่เกิดขึ้นในแบบสอบถามเหล่านี้ทั้งหมด หมายเหตุอธิบายแผนซึ่งระบุว่าจะใช้ดัชนีเมื่อใดและเมื่อใด

2. ใช้นิพจน์ทั่วไป

จาก Oracle 10g เป็นต้นไปREGEXP_LIKE()สามารถใช้ได้ คุณสามารถระบุ _match_parameter_ 'i'เพื่อทำการค้นหาแบบตรงตามตัวพิมพ์ใหญ่ - เล็ก

ในการใช้สิ่งนี้เป็นตัวดำเนินการความเสมอภาคคุณต้องระบุจุดเริ่มต้นและจุดสิ้นสุดของสตริงซึ่งแสดงโดยกะรัตและเครื่องหมายดอลลาร์

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

ในการดำเนินการเทียบเท่า LIKE สามารถลบออกได้

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

โปรดระวังสิ่งนี้เนื่องจากสตริงของคุณอาจมีอักขระที่จะตีความแตกต่างกันโดยเอ็นจินนิพจน์ทั่วไป

ซอ Fiddle นี้แสดงผลลัพธ์ตัวอย่างเดียวกันให้คุณยกเว้นการใช้ REGEXP_LIKE ()

3. เปลี่ยนเป็นระดับเซสชัน

NLS_SORTพารามิเตอร์ควบคุมลำดับการเปรียบเทียบสำหรับการสั่งซื้อและดำเนินการเปรียบเทียบต่างๆรวมถึง=และไม่ชอบ คุณสามารถระบุไบนารีเป็นตัวพิมพ์เล็กและตัวพิมพ์ใหญ่การเรียงลำดับโดยการเปลี่ยนเซสชั่น ซึ่งหมายความว่าทุกแบบสอบถามที่ดำเนินการในเซสชันนั้นจะดำเนินการตามตัวพิมพ์เล็กและใหญ่

alter session set nls_sort=BINARY_CI

มีข้อมูลเพิ่มเติมมากมายเกี่ยวกับการเรียงลำดับภาษาศาสตร์และการค้นหาสตริงหากคุณต้องการระบุภาษาอื่นหรือทำการค้นหาแบบเน้นเสียงโดยใช้ BINARY_AI

คุณจะต้องเปลี่ยนพารามิเตอร์NLS_COMPด้วย อ้าง:

ตัวดำเนินการที่แน่นอนและส่วนคำสั่งที่ปฏิบัติตามพารามิเตอร์ NLS_SORT ขึ้นอยู่กับค่าของพารามิเตอร์ NLS_COMP หากโอเปอเรเตอร์หรือส่วนคำสั่งไม่เชื่อฟังค่า NLS_SORT ตามที่กำหนดโดย NLS_COMP การเปรียบเทียบที่ใช้คือ BINARY

ค่าเริ่มต้นของ NLS_COMP คือ BINARY แต่ LINGUISTIC ระบุว่า Oracle ควรใส่ใจกับค่าของ NLS_SORT:

การเปรียบเทียบสำหรับการดำเนินงาน SQL ทั้งหมดในส่วนคำสั่ง WHERE และบล็อก PL / SQL ควรใช้การเรียงลำดับภาษาศาสตร์ที่ระบุในพารามิเตอร์ NLS_SORT เพื่อปรับปรุงประสิทธิภาพคุณยังสามารถกำหนดดัชนีภาษาในคอลัมน์ที่คุณต้องการเปรียบเทียบภาษา

ดังนั้นอีกครั้งคุณจะต้องเปลี่ยนเซสชั่น

alter session set nls_comp=LINGUISTIC

ดังที่ระบุไว้ในเอกสารคุณอาจต้องการสร้างดัชนีภาษาเพื่อปรับปรุงประสิทธิภาพ

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

"สร้างดัชนีตามฟังก์ชั่น" สร้างความแตกต่างได้อย่างน่าทึ่ง
Jacob Goulden

ผมอาจจะถามว่าทำไมมันเป็นเรื่องที่แตกต่างกันที่จะทำ select * from my_table where lower(column_1) LIKE lower('my_string') || '%';แทน select * from my_table where lower(column_1) LIKE lower('my_string%');? มันให้ประโยชน์ใด ๆ หรือไม่?
lopezvit

1
เหตุผลหนึ่งก็คือหากการค้นหาของคุณถูก paramerterized (น่าจะเป็นในสถานการณ์ส่วนใหญ่) รหัสการโทรของคุณไม่จำเป็นต้องเชื่อมต่อกับ% ที่ท้าย @lopezvit เสมอ
เบ็

1
หากมีอักขระบางตัวที่จะยุ่งขึ้นจากผลของการregexp_likeจะมีวิธีที่จะหลบหนีสตริงดังกล่าวหรือไม่ ให้ตัวอย่างถ้าสตริงมี $ ผลลัพธ์จะไม่เป็นอย่างที่เราคาดหวัง // cc @Ben และคนอื่น ๆ โปรดแชร์
bozzmob

2
` เป็นตัวหนี @bozzmob ไม่ควรมีเอาต์พุตที่แตกต่างกันหากสตริงที่นิพจน์ทั่วไปทำงานอยู่มี a $ซึ่งอาจทำให้คุณเกิดปัญหาหากคุณต้องการ$ตัวอักษรในนิพจน์ปกติของคุณ หากคุณมีปัญหาที่เฉพาะเจาะจงฉันถามคำถามอื่นถ้าความคิดเห็น / คำตอบนี้ไม่ได้ช่วย
Ben

51

บางทีคุณสามารถลองใช้

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

3
ใช้งานได้เมื่อพารามิเตอร์อินพุตเป็นตัวพิมพ์ใหญ่ทั้งหมดและหากตัวพิมพ์เล็กหรือผสมกันจะไม่ทำงาน
sergionni

13
คุณเคยคิดWHERE upper(user_name) LIKE UPPER('%ME%')บ้างไหม? :)
Konerak

3
@sergionni คุณต้องค้นหาด้วยข้อความค้นหาเช่นกัน!
Markus Winand

3
@ sergionni งั้นทำไมคุณไม่ใช้UPPERพารามิเตอร์อินพุตด้วยล่ะ
Czechnology

5
@ V4Vendetta ใช้upperฟังก์ชั่นที่คุณสูญเสียดัชนีคุณมีความคิดวิธีการค้นหาโดยใช้ดัชนีหรือไม่?
jcho360

7

จาก Oracle 12c R2 คุณสามารถใช้COLLATE operator:

COLLATE โอเปอเรเตอร์กำหนดการเรียงหน้าสำหรับนิพจน์ โอเปอเรเตอร์นี้ช่วยให้คุณสามารถแทนที่การเปรียบเทียบที่ฐานข้อมูลจะได้มาสำหรับนิพจน์โดยใช้กฎการได้มาซึ่งการเปรียบเทียบการจัดเรียงมาตรฐาน

ตัวดำเนินการ COLLATE ใช้เวลาหนึ่งอาร์กิวเมนต์ collation_name ซึ่งคุณสามารถระบุการเปรียบเทียบที่กำหนดชื่อหรือการตรวจสอบเทียมหลอก หากชื่อการเรียงมีช่องว่างคุณต้องใส่ชื่อในเครื่องหมายคำพูดคู่

การสาธิต:

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 <> การสาธิตซอ


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

%'s ในอาร์กิวเมนต์แรกที่สองของคุณNLSSORTจะไม่ได้หมายถึงการเป็นสัญลักษณ์แทนใช่มั้ย? พวกเขาค่อนข้างสับสน
Stefan van den Akker

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.