Búsqueda insensible a mayúsculas y minúsculas en Oracle

228

El comportamiento predeterminado de LIKEy los otros operadores de comparación, =etc. distingue entre mayúsculas y minúsculas.

¿Es posible hacerlos insensibles a mayúsculas y minúsculas?

sergionni
fuente
Recordatorio amigable de que algunas de las búsquedas de ejemplo darán como resultado un escaneo completo de la tabla, incluso si hay un índice en user_name.
JonSG
8
¿Has considerado usar en REGEXP_LIKE(username,'me','i')lugar de LIKE?
kubanczyk
55
no, LIKE funciona bien para mí
sergionni

Respuestas:

82

Desde 10gR2, Oracle permite ajustar el comportamiento de las comparaciones de cadenas configurando los parámetros de sesión NLS_COMPy 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

También puede crear índices sin distinción entre mayúsculas y minúsculas:

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

Esta información fue tomada de búsquedas insensibles a mayúsculas y minúsculas de Oracle . El artículo menciona, REGEXP_LIKEpero parece funcionar con buenos viejos =también.


En versiones anteriores a 10gR2, realmente no se puede hacer y el enfoque habitual, si no necesita una búsqueda insensible al acento , es solo UPPER()la columna y la expresión de búsqueda.

Álvaro González
fuente
1
Esto funciona bien, pero hace que las ACTUALIZACIONES que usan los operadores LIKE / = sean muy lentas ...... :(
Saqib Ali
1
@SaqibAli Las LIKEexpresiones arbitrarias (p WHERE foo LIKE '%abc%'. Ej. ) Ya son lo suficientemente lentas si no pueden indexarse, no creo que esté específicamente relacionado con mayúsculas y minúsculas.
Álvaro González
1
También puede configurarlos fuera de SQLPLUS, como en el entorno de shell. Por ejemplo, en un script Perl usando DBD::Oracle, puede escribir $ENV{NLS_SORT} = 'BINARY_CI'; $ENV{NLS_COMP} = 'LINGUISTIC';antes de llamar a `DBI-> connect`.
mivk
oye, ALTER SESSIONsolo altera su instancia local de la corrección y significa que es como su sesión actual, es decir, si cierro y vuelvo a abrir, se habría restablecido. ¿Hay alguna manera de que pueda ver cuáles son los valores actuales para que si persiste en todas partes pueda volver a la configuración original ...
Seabizkit
305

Hay 3 formas principales de realizar una búsqueda sin distinción entre mayúsculas y minúsculas en Oracle sin usar índices de texto completo.

En última instancia, el método que elija depende de sus circunstancias individuales; Lo principal que debe recordar es que para mejorar el rendimiento debe indexar correctamente las búsquedas que no distinguen entre mayúsculas y minúsculas.

1. Coloque su columna y su cadena de manera idéntica.

Puede forzar que todos sus datos sean el mismo caso usando UPPER()o LOWER():

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

o

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

Si column_1no está indexado upper(column_1)o lower(column_1), según corresponda, esto puede forzar un escaneo completo de la tabla. Para evitar esto, puede crear un índice basado en funciones .

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

Si usa LIKE, debe concatenar una %cadena alrededor de la cadena que está buscando.

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

Este SQL Fiddle demuestra lo que sucede en todas estas consultas. Tenga en cuenta los planes de explicación, que indican cuándo se utiliza un índice y cuándo no.

2. Usa expresiones regulares.

A partir de Oracle 10g en adelante REGEXP_LIKE()está disponible. Puede especificar _match_parameter_ 'i'para realizar búsquedas que no distinguen entre mayúsculas y minúsculas.

Para usar esto como un operador de igualdad, debe especificar el inicio y el final de la cadena, que se denota por el quilate y el signo de dólar.

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

Para realizar el equivalente de LIKE, estos se pueden eliminar.

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

Tenga cuidado con esto ya que su cadena puede contener caracteres que serán interpretados de manera diferente por el motor de expresión regular.

Este Fiddle de SQL le muestra el mismo resultado de ejemplo, excepto que usa REGEXP_LIKE ().

3. Cámbielo a nivel de sesión.

El parámetro NLS_SORT gobierna la secuencia de clasificación para ordenar y los diversos operadores de comparación, incluidos =y LIKE. Puede especificar un orden binario, sin distinción entre mayúsculas y minúsculas, ordenando alterando la sesión. Esto significará que cada consulta realizada en esa sesión realizará parámetros que no distinguen entre mayúsculas y minúsculas.

alter session set nls_sort=BINARY_CI

Hay mucha información adicional sobre la clasificación lingüística y la búsqueda de cadenas si desea especificar un idioma diferente, o hacer una búsqueda insensible al acento usando BINARY_AI.

También deberá cambiar el parámetro NLS_COMP ; citar:

Los operadores exactos y las cláusulas de consulta que obedecen al parámetro NLS_SORT dependen del valor del parámetro NLS_COMP. Si un operador o cláusula no obedece el valor NLS_SORT, según lo determinado por NLS_COMP, la intercalación utilizada es BINARY.

El valor predeterminado de NLS_COMP es BINARY; pero, LINGUISTIC especifica que Oracle debe prestar atención al valor de NLS_SORT:

Las comparaciones para todas las operaciones SQL en la cláusula WHERE y en los bloques PL / SQL deben usar el tipo lingüístico especificado en el parámetro NLS_SORT. Para mejorar el rendimiento, también puede definir un índice lingüístico en la columna para la que desea realizar comparaciones lingüísticas.

Entonces, una vez más, necesitas alterar la sesión

alter session set nls_comp=LINGUISTIC

Como se señala en la documentación, es posible que desee crear un índice lingüístico para mejorar el rendimiento

create index my_linguistc_index on my_table 
   (NLSSORT(column_1, 'NLS_SORT = BINARY_CI'));
Ben
fuente
"crear un índice basado en funciones" Increíble la diferencia que esto puede hacer
Jacob Goulden
¿Puedo preguntar por qué es diferente hacer en select * from my_table where lower(column_1) LIKE lower('my_string') || '%';lugar de select * from my_table where lower(column_1) LIKE lower('my_string%');? ¿Le da alguna ventaja?
lopezvit
1
Una razón sería si su consulta está paramerterizada (probablemente en la mayoría de las situaciones), entonces su código de llamada no necesita concatenar siempre un% al final @lopezvit.
Ben
1
Si hay algunos caracteres que estropearán el resultado regexp_like, ¿hay alguna forma de escapar de tales cadenas? Dando un ejemplo, si la cadena tiene $, la salida no será la que esperamos. // cc @Ben y otros, por favor comparte.
bozzmob
2
` es el personaje de escape @bozzmob. No debería haber ninguna diferencia en la salida si la cadena en la que opera la expresión regular contiene a $, esto solo puede causar problemas si necesita un $literal en su expresión regular. Si tiene un problema específico, haría otra pregunta si este comentario / respuesta no ha ayudado.
Ben
51

tal vez puedas intentar usar

SELECT user_name
FROM user_master
WHERE upper(user_name) LIKE '%ME%'
V4Vendetta
fuente
3
funciona cuando el parámetro de entrada es todo en mayúsculas, y si es inferior o mixto no lo hace
sergionni
13
¿Has pensado en eso WHERE upper(user_name) LIKE UPPER('%ME%')entonces? :)
Konerak
3
@sergionni, también debes poner en mayúscula el término de búsqueda.
Markus Winand
3
@sergionni, bueno, ¿por qué no usas UPPERtambién el parámetro de entrada?
Czechnology
55
@ V4Vendetta usando la upperfunción pierde el índice, ¿tiene alguna idea de cómo hacer una búsqueda usando el índice?
jcho360
7

Desde Oracle 12c R2 puedes usar COLLATE operator:

El operador COLLATE determina la clasificación para una expresión. Este operador le permite anular la clasificación que la base de datos habría derivado para la expresión utilizando reglas de derivación de clasificación estándar.

El operador COLLATE toma un argumento, collation_name, para el que puede especificar una colación con nombre o una pseudocolación. Si el nombre de intercalación contiene un espacio, debe encerrarlo entre comillas dobles.

Manifestación:

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 <> demostración de violín

Lukasz Szozda
fuente
2
select user_name
from my_table
where nlssort(user_name, 'NLS_SORT = Latin_CI') = nlssort('%AbC%', 'NLS_SORT = Latin_CI')
Clodoaldo Neto
fuente
Los %'s en el primer argumento a su segundo noNLSSORT están destinados a ser comodines, ¿verdad? Se confunden un poco.
Stefan van den Akker
1

puedes hacer algo así:

where regexp_like(name, 'string$', 'i');
grep
fuente