Recherche insensible à la casse dans Oracle

228

Le comportement par défaut de LIKEet des autres opérateurs de comparaison, =etc. est sensible à la casse.

Est-il possible de les rendre insensibles à la casse?

sergionni
la source
Rappel amical que certaines des recherches d'exemple entraîneront une analyse complète de la table même s'il existe un index sur nom_utilisateur.
JonSG
8
Avez-vous envisagé d'utiliser REGEXP_LIKE(username,'me','i')au lieu de LIKE?
kubanczyk
5
non, LIKE fonctionne bien pour moi
sergionni

Réponses:

82

Depuis 10gR2, Oracle permet d'affiner le comportement des comparaisons de chaînes en définissant les paramètres de session NLS_COMPet 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

Vous pouvez également créer des index insensibles à la casse:

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

Ces informations proviennent de recherches insensibles à la casse d' Oracle . L'article mentionne REGEXP_LIKEmais il semble fonctionner aussi avec du bon vieux =.


Dans les versions plus anciennes que 10gR2, cela ne peut pas vraiment être fait et l'approche habituelle, si vous n'avez pas besoin d'une recherche insensible aux accents, consiste à utiliser à la UPPER()fois la colonne et l'expression de recherche.

Álvaro González
la source
1
Cela fonctionne bien, mais cela rend les mises à jour utilisant les opérateurs LIKE / = très lentes ...... :(
Saqib Ali
1
@SaqibAli Les LIKEexpressions arbitraires (par exemple WHERE foo LIKE '%abc%') sont déjà suffisamment lentes si elles ne peuvent pas être indexées, je ne pense pas que ce soit spécifiquement lié à la sensibilité à la casse.
Álvaro González
1
Vous pouvez également les définir en dehors de SQLPLUS, comme dans l'environnement shell. Par exemple, dans un script Perl utilisant DBD::Oracle, vous pouvez écrire $ENV{NLS_SORT} = 'BINARY_CI'; $ENV{NLS_COMP} = 'LINGUISTIC';avant d'appeler `DBI-> connect`.
mivk
hey ne fait ALTER SESSIONque modifier votre instance locale de la correction et cela signifie-t-il que votre session actuelle, c'est-à-dire que si je ferme et rouvre, elle aurait réinitialisé. Existe-t-il un moyen de voir quelles sont les valeurs actuelles de sorte que si sa persiste partout, je puisse revenir aux paramètres d'origine ...
Seabizkit
305

Il existe 3 méthodes principales pour effectuer une recherche insensible à la casse dans Oracle sans utiliser d'index de texte intégral.

En fin de compte, la méthode que vous choisissez dépend de votre situation individuelle; la principale chose à retenir est que pour améliorer les performances, vous devez indexer correctement pour la recherche insensible à la casse.

1. Casez votre colonne et votre chaîne de manière identique.

Vous pouvez forcer toutes vos données à être le même cas en utilisant 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');

S'il column_1n'est pas indexé upper(column_1)ou lower(column_1), le cas échéant, cela peut forcer une analyse complète de la table. Pour éviter cela, vous pouvez créer un index basé sur les fonctions .

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

Si vous utilisez LIKE, vous devez concaténer un %autour de la chaîne que vous recherchez.

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

Ce SQL Fiddle montre ce qui se passe dans toutes ces requêtes. Notez les plans d'explication, qui indiquent quand un index est utilisé et quand il ne l'est pas.

2. Utilisez des expressions régulières.

À partir d'Oracle 10g et plus REGEXP_LIKE()est disponible. Vous pouvez spécifier le _match_parameter_ 'i', afin d'effectuer une recherche insensible à la casse.

Pour l'utiliser comme opérateur d'égalité, vous devez spécifier le début et la fin de la chaîne, qui est indiquée par le carat et le signe dollar.

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

Afin de réaliser l'équivalent de LIKE, ceux-ci peuvent être supprimés.

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

Soyez prudent avec cela car votre chaîne peut contenir des caractères qui seront interprétés différemment par le moteur d'expression régulière.

Ce SQL Fiddle vous montre le même exemple de sortie, sauf en utilisant REGEXP_LIKE ().

3. Modifiez-le au niveau de la session.

Le paramètre NLS_SORT régit la séquence de classement pour la commande et les divers opérateurs de comparaison, y compris =et LIKE. Vous pouvez spécifier un tri binaire, insensible à la casse, en modifiant la session. Cela signifie que chaque requête effectuée dans cette session exécutera des paramètres insensibles à la casse.

alter session set nls_sort=BINARY_CI

Il y a beaucoup d'informations supplémentaires sur le tri linguistique et la recherche de chaînes si vous souhaitez spécifier une autre langue ou effectuer une recherche insensible aux accents à l'aide de BINARY_AI.

Vous devrez également modifier le paramètre NLS_COMP ; citer:

Les opérateurs et clauses de requête exacts qui obéissent au paramètre NLS_SORT dépendent de la valeur du paramètre NLS_COMP. Si un opérateur ou une clause n'obéit pas à la valeur NLS_SORT, telle que déterminée par NLS_COMP, le classement utilisé est BINARY.

La valeur par défaut de NLS_COMP est BINARY; mais, LINGUISTIC spécifie qu'Oracle doit prêter attention à la valeur de NLS_SORT:

Les comparaisons pour toutes les opérations SQL dans la clause WHERE et dans les blocs PL / SQL doivent utiliser le tri linguistique spécifié dans le paramètre NLS_SORT. Pour améliorer les performances, vous pouvez également définir un index linguistique sur la colonne pour laquelle vous souhaitez des comparaisons linguistiques.

Donc, encore une fois, vous devez modifier la session

alter session set nls_comp=LINGUISTIC

Comme indiqué dans la documentation, vous souhaiterez peut-être créer un index linguistique pour améliorer les performances

create index my_linguistc_index on my_table 
   (NLSSORT(column_1, 'NLS_SORT = BINARY_CI'));
Ben
la source
"créer un index basé sur les fonctions" Incroyable quelle différence cela peut faire
Jacob Goulden
Puis-je demander pourquoi il est différent de faire select * from my_table where lower(column_1) LIKE lower('my_string') || '%';au lieu de select * from my_table where lower(column_1) LIKE lower('my_string%');? Cela donne-t-il un avantage?
lopezvit
1
Une raison serait que si votre requête est paramétrisée (probablement dans la plupart des situations), votre code appelant n'a pas besoin de toujours concaténer un% à la fin de @lopezvit.
Ben
1
S'il y a des personnages qui gâcheront le résultat regexp_like, y a-t-il un moyen d'échapper à de telles chaînes? Donnant un exemple, si la chaîne a $, la sortie ne sera pas celle que nous attendons. // cc @Ben et autres, merci de partager.
bozzmob
2
` est le personnage d'échappement @bozzmob. Il ne devrait pas y avoir de différence de sortie si la chaîne sur laquelle l'expression régulière fonctionne contient un $, cela ne peut vous poser de problèmes que si vous avez besoin d'un $littéral dans votre expression régulière. Si vous avez un problème spécifique, je poserais une autre question si ce commentaire / réponse n'a pas aidé.
Ben
51

vous pouvez peut-être essayer d'utiliser

SELECT user_name
FROM user_master
WHERE upper(user_name) LIKE '%ME%'
V4Vendetta
la source
3
cela fonctionne lorsque le paramètre d'entrée est entièrement en majuscules, et s'il est inférieur ou mélangé, il ne fonctionne pas
sergionni
13
Avez-vous pensé WHERE upper(user_name) LIKE UPPER('%ME%')alors? :)
Konerak
3
@sergionni vous devez également mettre en majuscule le terme de recherche!
Markus Winand
3
@sergionni, eh bien pourquoi n'utilisez-vous pas UPPERaussi le paramètre d'entrée?
Czechnology
5
@ V4Vendetta en utilisant la upperfonction vous perdez l'index, avez-vous une idée de comment faire une recherche en utilisant l'index?
jcho360
7

Depuis Oracle 12c R2, vous pouvez utiliser COLLATE operator:

L'opérateur COLLATE détermine le classement d'une expression. Cet opérateur vous permet de remplacer le classement que la base de données aurait dérivé pour l'expression à l'aide de règles de dérivation de classement standard.

L'opérateur COLLATE prend un argument, collation_name, pour lequel vous pouvez spécifier un classement nommé ou un pseudo-classement. Si le nom du classement contient un espace, vous devez placer le nom entre guillemets doubles.

Démo:

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"
*/

démo db <> fiddle

Lukasz Szozda
la source
2
select user_name
from my_table
where nlssort(user_name, 'NLS_SORT = Latin_CI') = nlssort('%AbC%', 'NLS_SORT = Latin_CI')
Clodoaldo Neto
la source
Les %dans le premier argument du second neNLSSORT sont pas censés être des caractères génériques, non? Ils confondent en quelque sorte.
Stefan van den Akker
1

vous pouvez faire quelque chose comme ça:

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