Comment définir Sqlite3 pour qu'il soit insensible à la casse lors de la comparaison de chaînes?

305

Je veux sélectionner des enregistrements de la base de données sqlite3 par correspondance de chaîne. Mais si j'utilise '=' dans la clause where, j'ai trouvé que sqlite3 est sensible à la casse. Quelqu'un peut-il me dire comment utiliser une chaîne en comparant la casse?

quantité
la source

Réponses:

493

Vous pouvez utiliser COLLATE NOCASEdans votre SELECTrequête:

SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE

De plus, dans SQLite, vous pouvez indiquer qu'une colonne doit être insensible à la casse lorsque vous créez la table en spécifiant collate nocasedans la définition de colonne (les autres options sont binary(par défaut) et rtrim; voir ici ). Vous pouvez également spécifier collate nocasequand vous créez un index. Par exemple:

créer un test de table
(
  Text_Value text collate nocase
);

insérer dans les valeurs de test («A»);
insérer dans les valeurs de test («b»);
insérer dans les valeurs de test («C»);

créer un index Test_Text_Value_Index
  sur Test (Text_Value collate nocase);

Les expressions impliquant Test.Text_Valuedoivent désormais être insensibles à la casse. Par exemple:

sqlite> sélectionnez Text_Value dans Test où Text_Value = 'B';
Text_Value      
----------------
b               

sqlite> sélectionnez Text_Value dans Test order by Text_Value;
Text_Value      
----------------
UNE               
b               
C    

sqlite> sélectionnez Text_Value dans Test order by Text_Value desc;
Text_Value      
----------------
C               
b               
UNE               

L'optimiseur peut également potentiellement utiliser l'index pour la recherche et la correspondance insensibles à la casse sur la colonne. Vous pouvez le vérifier en utilisant la explaincommande SQL, par exemple:

sqlite> expliquer sélectionner Text_Value dans Test où Text_Value = 'b';
addr opcode p1 p2 p3                               
---------------- -------------- ---------- ---------- ---------------------------------
0 Aller 0 16                                           
1 Entier 0 0                                            
2 OpenRead 1 3 keyinfo (1, NOCASE)                
3 SetNumColumns 1 2                                            
4 cordes8 0 0 b                                
5 IsNull -1 14                                           
6 MakeRecord 1 0 a                                
7 MemStore 0 0                                            
8 MoveGe 1 14                                           
9 MemLoad 0 0                                            
10 IdxGE 1 14 +                                
11 Colonne 1 0                                            
12 Rappel 1 0                                            
13 Suivant 1 9                                            
14 Fermer 1 0                                            
15 Arrêt 0 0                                            
16 Transaction 0 0                                            
17 VerifyCookie 0 4                                            
18 Goto 0 1                                            
19 Noop 0 0                                            
cheduardo
la source
20
Après avoir (re) créé la table avec 'COLLATE NOCASE', j'ai remarqué que c'était beaucoup plus rapide que la requête WHERE nom = 'quelqu'un' COLLATE NOCASE. BEAUCOUP plus rapide (six à 10 fois, à peu près?)
DefenestrationDay
10
Selon la documentation, l'ajout COLLATE NOCASEà l'index n'est pas requis si le champ lui-même a déjà ce classement défini: " La séquence de classement par défaut est la séquence de classement définie pour cette colonne dans l'instruction CREATE TABLE. "
Heinzi
29
COLLATE NOCASEne fonctionnera qu'avec du texte ASCII. Une fois que vous avez "FIANCÉ" ou "voilà" dans vos valeurs de colonne, il ne correspondra pas à "fiancé" ou "VOILA". Après avoir activé l'extension ICU, LIKEdevient insensible à la casse , 'FIANCÉ' LIKE 'fiancé'c'est vrai, mais 'VOILA' LIKE 'voilà'toujours faux. Et ICU + LIKE a l'inconvénient de ne pas utiliser l'index, il peut donc être lent sur les grandes tables.
sélectionnez div, cas où div = 'fail' puis 'FAIL' sinon 'PASSED' end, * à partir des marques collation nocase ci-dessus n'a pas fonctionné est-ce que je fais quelque chose de mal?
Thunder
7
Une chose à noter qui m'a fait trébucher: select * from tbl where firstname='john' and lastname='doe' COLLATE NOCASEsera insensible à la casse lastname. Pour être insensible à la casse sur firstname, écrire ceci: select * from tbl where firstname='john' COLLATE NOCASE and lastname='doe'. C'est spécifique à cette colonne, pas à la whereclause entière .
James Toomey
148
SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE
Craz
la source
5
Si vous êtes comme moi et que vous souhaitez plus de documentation sur l'assemblage, vous pouvez le trouver ici sur cette page: sqlite.org/datatype3.html Faites défiler jusqu'à # 6.0
Will
47

Vous pouvez le faire comme ceci:

SELECT * FROM ... WHERE name LIKE 'someone'

(Ce n'est pas la solution, mais dans certains cas, c'est très pratique)

"L' opérateur LIKE effectue une comparaison de correspondance de modèles. L'opérande de droite contient le modèle, l'opérande de gauche contient la chaîne à comparer au modèle. Un symbole de pourcentage ("% ") dans le modèle correspond à n'importe quelle séquence de zéro ou plus. caractères dans la chaîne. Un trait de soulignement ("_") dans le modèle correspond à n'importe quel caractère de la chaîne. Tout autre caractère correspond à lui-même ou à son équivalent en minuscules / majuscules (c.-à-d. correspondance insensible à la casse ) . (Un bogue: SQLite ne comprend que majuscules / minuscules pour les caractères ASCII. L'opérateur LIKE est sensible à la casse pour les caractères unicode qui se trouvent au-delà de la plage ASCII. Par exemple, l'expression «a» LIKE «A» est VRAIE mais «æ» LIKE «Æ»c'est faux.)."

Nick Dandoulakis
la source
@ MM-BB oui, à moins que nous n'effectuions le LIKE sur une colonne qui est déclarée (ou indexée) comme COLLATE NOCASE, il effectuera une analyse complète des lignes.
Nick Dandoulakis
1
Ce n'est pas un bug, c'est une limitation documentée. La même page citée dans la réponse mentionne l'extension ICU qui gère les caractères unicode. (Peut-être que ce n'était pas le cas en 2009)
stenci
40

Ce n'est pas spécifique à sqlite mais vous pouvez simplement le faire

SELECT * FROM ... WHERE UPPER(name) = UPPER('someone')
oscarkuo
la source
L'autre partie du problème de performances est de trouver les lignes correspondantes dans le tableau. SQLite3 prend-il en charge les index basés sur les fonctions? L'indexation de la colonne ou de l'expression de recherche (par exemple "UPPER (nom)") dans une situation comme celle-ci est généralement une bonne idée.
cheduardo
13
Attention à celui-ci, comme l'a laissé entendre cheduardo, SQLite ne peut pas utiliser d'index sur 'nom' lors de l'exécution de cette requête. Le moteur de base de données devra analyser toutes les lignes en entier, convertir tous les champs «nom» en majuscules et exécuter la comparaison.
Mathew Waters
1
@quantity, oui, beaucoup.
Le Berga
4

Une autre option consiste à créer votre propre classement personnalisé. Vous pouvez ensuite définir ce classement sur la colonne ou l'ajouter à vos clauses de sélection. Il sera utilisé pour les commandes et les comparaisons.

Cela peut être utilisé pour faire «VOILA» COMME «voilà».

http://www.sqlite.org/capi3ref.html#sqlite3_create_collation

La fonction de classement doit renvoyer un entier négatif, nul ou positif si la première chaîne est respectivement inférieure, égale ou supérieure à la seconde.

Nick Ericson
la source
2

Une autre option qui peut ou non avoir un sens dans votre cas, est d'avoir en fait une colonne séparée avec des valeurs pré-notées de votre colonne existante. Cela peut être rempli à l'aide de la fonction SQLite LOWER(), et vous pouvez ensuite effectuer une correspondance sur cette colonne à la place.

Évidemment, cela ajoute de la redondance et un potentiel d'incohérence, mais si vos données sont statiques, cela pourrait être une option appropriée.

Magnus W
la source
2

Simplement, vous pouvez utiliser COLLATE NOCASE dans votre requête SELECT:

SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE
Pullat Junaid
la source
1

Si la colonne est de type, charvous devez ajouter la valeur que vous interrogez avec des espaces, veuillez vous référer à cette question ici . Ceci en plus d'utiliser COLLATE NOCASEou l'une des autres solutions (upper (), etc).

A AlTaiar
la source
0

vous pouvez utiliser la requête similaire pour comparer la chaîne respective aux valeurs de table.

sélectionnez le nom de la colonne dans nom_table où le nom de la colonne comme «valeur de comparaison respective»;

Mahendranatarajan
la source
Cela n'ajoute rien à stackoverflow.com/a/973665/2462516 qui a été publié en 2009
umasudhan
0

Cela fonctionne parfaitement pour moi. SELECT NAME FROM TABLE_NAME WHERE NAME = 'test Name' COLLATE NOCASE

Shohel Rana
la source