Пошук нечутливих випадків в 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')
   )
;

Ця інформація була взята з нечутливих пошукових випадків Oracle . У статті згадується, REGEXP_LIKEале, здається, працює і з добрим старим =.


У версіях, старших від 10gR2, це реально неможливо зробити, і звичайний підхід, якщо вам не потрібен нечутливий до акцентів , - це простоUPPER() стовпець і вираз пошуку.


1
Це працює добре, але це робить ОНОВЛЕННЯ за допомогою операторів LIKE / = дуже повільними ...... :(
Saqib Ali

1
@SaqibAli Довільні LIKEвирази (наприклад WHERE foo LIKE '%abc%') вже досить повільні, якщо їх неможливо індексувати, я не думаю, що це спеціально пов'язане з чутливістю до регістру.
Альваро Гонсалес

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, вам доведеться об'єднати a % навколо себе рядок, який ви шукаєте.

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

Цей скрипт SQL демонструє, що відбувається у всіх цих запитах. Зверніть увагу на Плани пояснення, які вказують, коли використовується індекс, а коли - ні.

2. Використовуйте регулярні вирази.

Від Oracle 10г і далі 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');

Будьте обережні з цим, оскільки ваша рядок може містити символи, які будуть по-різному інтерпретовані механізмом регулярних виразів.

Цей скрипт SQL показує той самий приклад виводу, за винятком використання REGEXP_LIKE ().

3. Змініть його на рівні сеансу.

Параметр NLS_SORT керує послідовністю зіставлення для замовлення та різними операторами порівняння, включаючи =та LIKE. Ви можете вказати бінарний, нечутливий до регістру сортування, змінивши сеанс. Це буде означати, що кожен запит, виконаний на цьому сеансі, виконуватиме нечутливі до регістру параметри.

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'));

"Створити функціональний індекс" Дивовижне, що може змінити це
Якоб Гулден

Чи можу я запитати, чому це інакше робити 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
Однією з причин може бути, якщо ваш запит параметризований (можливо, у більшості ситуацій), тоді ваш код виклику не потрібно завжди об'єднувати% в кінці @lopezvit.
Бен

1
Якщо є деякі символи, які зіпсують результат regexp_like, чи є спосіб уникнути таких рядків? Надаючи приклад, якщо рядок має $, вихід буде не таким, як ми очікуємо. // cc @Ben та інші, будь ласка, діліться.
bozzmob

2
` є символом втечі @bozzmob. Не повинно бути різниць у виведенні, якщо рядок, над якою працює регулярний вираз, містить $, це може спричинити проблеми лише в тому випадку, якщо вам потрібен $літерал у регулярному виразі. Якщо у вас є конкретна проблема, я б задав інше питання, якщо цей коментар / відповідь не допомогла.
Бен

51

можливо, ви можете спробувати використовувати

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

3
він працює, коли вхідний параметр є
великим великим

13
Ви думали про це WHERE upper(user_name) LIKE UPPER('%ME%')тоді? :)
Конерак

3
@sergionni Ви також повинні мати верхній регістр пошукового терміну!
Маркус Вінанд

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є НЕ означає бути підстановочних, вірно? Вони свого роду плутають.
Стефан ван ден Аккер

1

ви можете зробити щось подібне:

where regexp_like(name, 'string$', 'i');
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.