Поиск без учета регистра в Oracle

228

Поведение по умолчанию LIKEи других операторов сравнения и =т. Д. Чувствительно к регистру.

Можно ли сделать их без учета регистра?

sergionni
источник
Дружеское напоминание о том, что некоторые примеры поиска приведут к полному сканированию таблицы, даже если есть индекс для user_name.
JonSG
8
Рассматривали ли вы использовать REGEXP_LIKE(username,'me','i')вместо как, как?
Кубанчик
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 / = очень медленными ...... :(
Сакиб Али
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, вы должны объединить %вокруг искомой строки.

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

Эта скрипта SQL демонстрирует, что происходит во всех этих запросах. Обратите внимание на планы объяснения, которые указывают, когда индекс используется, а когда нет.

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

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

Эта SQL Fiddle показывает тот же пример вывода, за исключением использования 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%');? Это дает какое-то преимущество?
Лопезвит
1
Одной из причин может быть то, что если ваш запрос является параметризованным (вероятно, в большинстве случаев), тогда ваш код вызова не всегда должен объединять% в конце @lopezvit.
Бен
1
Если есть некоторые символы, которые могут испортить результат regexp_like, есть ли способ избежать таких строк? Например, если в строке есть $, результат будет не таким, как мы ожидаем. // cc @Ben и другие, пожалуйста, поделитесь.
bozzmob
2
` это escape-символ @bozzmob. Не должно быть никакой разницы в выводе, если строка, над которой работает регулярное выражение, содержит a $, это может вызвать проблемы только в том случае, если вам нужно $литерал в регулярном выражении. Если у вас есть конкретная проблема, я бы задал другой вопрос, если этот комментарий / ответ не помог.
Бен
51

может быть, вы можете попробовать использовать

SELECT user_name
FROM user_master
WHERE upper(user_name) LIKE '%ME%'
V4Vendetta
источник
3
это работает, когда входной параметр - весь верхний регистр, и если нижний или смешанный - нет
sergionni
13
Вы думали об этом WHERE upper(user_name) LIKE UPPER('%ME%')тогда? :)
Конерак
3
@sergionni, вы должны заглавные буквы, а также поисковый запрос!
Маркус Винанд
3
@sergionni, тогда почему бы тебе не использовать UPPERпараметр ввода тоже?
Чехословакия
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 <> Fiddle demo

Лукаш Шозда
источник
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');
Grep
источник