Pesquisa que não diferencia maiúsculas de minúsculas no Oracle


228

O comportamento padrão de LIKEe de outros operadores de comparação, =etc faz distinção entre maiúsculas e minúsculas.

É possível torná-los sem distinção entre maiúsculas e minúsculas?


Lembrete amigável de que algumas das pesquisas de exemplo resultarão em uma verificação completa da tabela, mesmo que exista um índice em nome_do_usuário.
— 22411 JonSG

8
Você já pensou em usar o REGEXP_LIKE(username,'me','i')LIKE?
— 22812 kubanczyk

5
não, COMO funciona ok para mim
— sergionni

Respostas:


82

Desde 10gR2, o Oracle permite ajustar o comportamento das comparações de cadeias, definindo os parâmetros NLS_COMPe NLS_SORTsession:

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

Você também pode criar índices que não diferenciam maiúsculas de minúsculas:

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

Essas informações foram obtidas de pesquisas que não diferenciam maiúsculas de minúsculas do Oracle . O artigo menciona, REGEXP_LIKEmas parece funcionar bem =também.


Nas versões anteriores a 10gR2, isso realmente não pode ser feito, e a abordagem usual, se você não precisar de pesquisa sem distinção de sotaque , é apenas UPPER()a coluna e a expressão de pesquisa.


1
Isso funciona bem, mas torna as atualizações usando os operadores LIKE / = muito lentas ...... :(
— Saqib Ali

1
@SaqibAli LIKEExpressões arbitrárias (por exemplo WHERE foo LIKE '%abc%') já são lentas o suficiente se não puderem ser indexadas, não acho que esteja especificamente relacionado à sensibilidade a casos.
— Álvaro González

1
Você também pode configurá-los fora do SQLPLUS, como no ambiente de shell. Por exemplo, em um script Perl usando DBD::Oracle, você pode escrever $ENV{NLS_SORT} = 'BINARY_CI'; $ENV{NLS_COMP} = 'LINGUISTIC';antes de chamar `DBI-> connect`.
— mivk

Ei, o ALTER SESSIONúnico altera sua instância local da correção e isso significa como sua sessão atual, ou seja, se eu fechar e reabrir, ela será redefinida. Existe uma maneira que eu possa ver o que os valores atuais são de modo que se a sua persistiu em todos os lugares que eu posso mudar de volta para as configurações originais ...
— Seabizkit

305

Existem três maneiras principais de executar uma pesquisa que não diferencia maiúsculas de minúsculas no Oracle sem usar índices de texto completo.

Em última análise, o método escolhido depende das circunstâncias individuais; o principal a lembrar é que, para melhorar o desempenho, você deve indexar corretamente a pesquisa que não diferencia maiúsculas de minúsculas.

1. Coloque sua coluna e sua string de forma idêntica.

Você pode forçar todos os seus dados a serem o mesmo caso usando UPPER()ou LOWER():

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

ou

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

Se column_1não estiver indexado upper(column_1)ou lower(column_1), conforme apropriado, isso poderá forçar uma verificação completa da tabela. Para evitar isso, você pode criar um índice baseado em funções .

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

Se você estiver usando o LIKE, precisará concatenar uma %sequência de caracteres que está procurando.

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

Este SQL Fiddle demonstra o que acontece em todas essas consultas. Observe os planos de explicação, que indicam quando um índice está sendo usado e quando não está.

2. Use expressões regulares.

A partir do Oracle 10g, REGEXP_LIKE()está disponível. Você pode especificar o _match_parameter_ 'i', para executar uma pesquisa que não diferencia maiúsculas de minúsculas.

Para usar isso como um operador de igualdade, você deve especificar o início e o fim da sequência, que é indicada pelo quilate e pelo sinal de dólar.

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

Para executar o equivalente a LIKE, eles podem ser removidos.

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

Tenha cuidado com isso, pois sua sequência pode conter caracteres que serão interpretados de maneira diferente pelo mecanismo de expressão regular.

Este SQL Fiddle mostra o mesmo exemplo de saída, exceto usando REGEXP_LIKE ().

3. Altere-o no nível da sessão.

O parâmetro NLS_SORT controla a sequência de intercalação para pedidos e os vários operadores de comparação, incluindo =e LIKE. Você pode especificar uma classificação binária, sem distinção entre maiúsculas e minúsculas, alterando a sessão. Isso significa que todas as consultas executadas nessa sessão executam parâmetros que não diferenciam maiúsculas de minúsculas.

alter session set nls_sort=BINARY_CI

Há muitas informações adicionais sobre classificação linguística e pesquisa de strings, se você deseja especificar um idioma diferente ou fazer uma pesquisa sem distinção de sotaque usando BINARY_AI.

Você também precisará alterar o parâmetro NLS_COMP ; citar:

Os operadores exatos e as cláusulas de consulta que obedecem ao parâmetro NLS_SORT dependem do valor do parâmetro NLS_COMP. Se um operador ou cláusula não obedecer ao valor NLS_SORT, conforme determinado por NLS_COMP, o agrupamento usado é BINARY.

O valor padrão de NLS_COMP é BINARY; mas, LINGUISTIC especifica que o Oracle deve prestar atenção ao valor de NLS_SORT:

As comparações para todas as operações SQL na cláusula WHERE e nos blocos PL / SQL devem usar a classificação linguística especificada no parâmetro NLS_SORT. Para melhorar o desempenho, você também pode definir um índice linguístico na coluna para o qual deseja comparações linguísticas.

Então, mais uma vez, você precisa alterar a sessão

alter session set nls_comp=LINGUISTIC

Conforme observado na documentação, você pode criar um índice linguístico para melhorar o desempenho

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

"criar um índice baseado em função" Amazing que diferença isso pode fazer
— Jacob Goulden

Posso perguntar por que é diferente fazer em select * from my_table where lower(column_1) LIKE lower('my_string') || '%';vez de select * from my_table where lower(column_1) LIKE lower('my_string%');? Isso dá alguma vantagem?
— Lopezvit

1
Uma razão seria se sua consulta fosse paramerizada (provavelmente na maioria das situações), então o código de chamada nem sempre precisa concatenar um% no final do @lopezvit.
— Ben

1
Se existem alguns personagens que irão atrapalhar o resultado regexp_like, existe uma maneira de escapar dessas seqüências? Dando um exemplo, se a string tiver $, a saída não será a esperada. // cc @Ben e outros, por favor, compartilhem.
— bozzmob

2
` é o caractere de escape @bozzmob. Não deve haver diferença na saída se a string na qual a expressão regular está operando contiver a $, isso só poderá causar problemas se você precisar de um $literal em sua expressão regular. Se você tiver um problema específico, eu faria outra pergunta se este comentário / resposta não ajudar.
— Ben

51

talvez você possa tentar usar

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

3
funciona quando o parâmetro de entrada está em maiúsculas e, se for menor ou misto, não funciona
— sergionni

13
Você já pensou sobre WHERE upper(user_name) LIKE UPPER('%ME%')isso? :)
— Konerak

3
@sergionni você também deve colocar em maiúscula o termo de pesquisa!
— Markus Winand

3
@ergionni, então por que você não usa UPPERo parâmetro de entrada também?
— Czecnologia

5
@ V4Vendetta usando a upperfunção de perder o índice, você tem alguma idéia de como fazer pesquisas usando o índice?
— Jcho360

7

No Oracle 12c R2, você poderia usar COLLATE operator:

O operador COLLATE determina o agrupamento de uma expressão. Esse operador permite substituir o agrupamento que o banco de dados derivaria para a expressão usando regras de derivação de agrupamento padrão.

O operador COLLATE usa um argumento, collation_name, para o qual você pode especificar um agrupamento nomeado ou pseudo-agrupamento. Se o nome do agrupamento contiver um espaço, coloque-o entre aspas duplas.

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 de violino


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

Os %'s no primeiro argumento para a sua segunda NLSSORTsão não pretende ser wildcards, certo? Eles meio que confundem.
— Stefan van den Akker

1

você pode fazer algo assim:

where regexp_like(name, 'string$', 'i');
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.