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?

sergionni
fonte
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.

Álvaro González
fonte
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'));
Ben
fonte
"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%'
V4Vendetta
fonte
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

Lukasz Szozda
fonte
2
select user_name
from my_table
where nlssort(user_name, 'NLS_SORT = Latin_CI') = nlssort('%AbC%', 'NLS_SORT = Latin_CI')
Clodoaldo Neto
fonte
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');
grep
fonte