Le serveur SQL ignore la casse dans une expression where

88

Comment puis-je construire une requête SQL (MS SQL Server) dans laquelle la clause «where» est insensible à la casse?

SELECT * FROM myTable WHERE myField = 'sOmeVal'

Je veux que les résultats reviennent en ignorant le cas

Raul Agrait
la source

Réponses:

136

Dans la configuration par défaut d'une base de données SQL Server, les comparaisons de chaînes ne sont pas sensibles à la casse. Si votre base de données remplace ce paramètre (via l'utilisation d'un autre classement), vous devrez spécifier le type de classement à utiliser dans votre requête.

SELECT * FROM myTable WHERE myField = 'sOmeVal' COLLATE SQL_Latin1_General_CP1_CI_AS

Notez que le classement que j'ai fourni n'est qu'un exemple (bien qu'il fonctionnera très probablement très bien pour vous). Un aperçu plus détaillé des classements SQL Server peut être trouvé ici .

Adam Robinson
la source
Juste pour confirmer, cela ne doit être ajouté qu'une seule fois, à la fin de la WHEREdéclaration, et affectera toutes les WHEREclauses, n'est-ce pas?
ashleedawg
Vous souhaitez savoir si votre réponse présente un problème de performances en convertissant une valeur de colonne en UPPERou en LOWERcas, puis en utilisant la LIKErecherche?
shaijut
1
@ashleedawg - bonne question .. cela semble être un paramètre par ligne.
Leo Gurdian
29

En règle générale, les comparaisons de chaînes ne sont pas sensibles à la casse. Si votre base de données est configurée pour un classement sensible à la casse, vous devez forcer à utiliser un classement insensible à la casse:

SELECT balance FROM people WHERE email = '[email protected]'
  COLLATE SQL_Latin1_General_CP1_CI_AS 
Andrejs Cainikovs
la source
@AskeB. et Andrejs: Ce n'est pas techniquement un problème de configuration de base de données. Veuillez consulter ma réponse pour obtenir des précisions sur les comparaisons de chaînes.
Solomon Rutzky
21

J'ai trouvé une autre solution ailleurs; c'est-à-dire utiliser

upper(@yourString)

mais tout le monde ici dit que, dans SQL Server, cela n'a pas d'importance car il ignore de toute façon la casse? Je suis presque sûr que notre base de données est sensible à la casse.

Danny
la source
6
Vous avez raison de dire qu'une base de données peut être sensible à la casse, mais c'est assez inefficace, même si c'est nécessaire. COLLATE est le mot-clé à utiliser.
mjaggard
1
Merci d'avoir soulevé cela, @mjaggard. J'espère que vous, ou quiconque semble rejeter ma réponse, élaborerez pour le bien de toute personne comme moi qui cherche et trouve des réponses comme la mienne.
Danny
1
J'ai voté pour cela car il s'agit d'une explication parfaitement rationnelle. L'assemblage sent trop de surcharge et que se passe-t-il si votre chaîne contient des caractères que le classement ne comprend pas? Latin 1 est un système de codage moche. Bonne chance pour obtenir des résultats significatifs si votre chaîne contient une apostrophe (comme: O'Brien).
eggmatters
2
Mieux aussi. Je peux penser à de nombreux cas où cela serait utile. De plus, il existe souvent plus d'une bonne façon de faire quelque chose.
Inversus le
1
Changer la casse de la chaîne à des fins de comparaison est généralement mauvais. Dans certaines langues, les conversions de cas ne sont pas aller-retour. c'est-à-dire INFÉRIEUR (x)! = INFÉRIEUR (SUPÉRIEUR (x)).
Ceisc
14

Les 2 premières réponses (d' Adam Robinson et Andrejs Cainikovs ) sont un peu, plutôt correctes, en ce sens qu'elles fonctionnent techniquement, mais leurs explications sont erronées et pourraient donc être trompeuses dans de nombreux cas. Par exemple, bien que le SQL_Latin1_General_CP1_CI_ASclassement fonctionne dans de nombreux cas, il ne doit pas être supposé être le classement insensible à la casse approprié. En fait, étant donné que l'OP fonctionne dans une base de données avec un classement sensible à la casse (ou éventuellement binaire), nous savons que l'OP n'utilise pas le classement par défaut pour tant d'installations (en particulier celles installées sur un OS en utilisant l' anglais américain comme langue): SQL_Latin1_General_CP1_CI_AS. Bien sûr, l'OP pourrait utiliser SQL_Latin1_General_CP1_CS_AS, mais lorsque vous travaillez avecVARCHARdonnées, il est important de ne pas changer la page de codes car cela pourrait entraîner une perte de données, et cela est contrôlé par la langue / la culture du classement (c'est-à-dire Latin1_General vs French vs Hebrew, etc.). Veuillez consulter le point 9 ci-dessous.

Les quatre autres réponses sont fausses à des degrés divers.

Je vais clarifier tous les malentendus ici afin que les lecteurs puissent, espérons-le, faire les choix les plus appropriés / efficaces.

  1. Ne l'utilisez pas UPPER(). C'est un travail supplémentaire complètement inutile. Utilisez une COLLATEclause. Une comparaison de chaînes doit être effectuée dans les deux cas, mais l'utilisation UPPER()doit également vérifier, caractère par caractère, s'il existe un mappage en majuscules, puis le modifier. Et vous devez le faire des deux côtés. L'ajout demande COLLATEsimplement au traitement de générer les clés de tri en utilisant un ensemble de règles différent de celui auquel il était destiné par défaut. L'utilisation COLLATEest nettement plus efficace (ou "performante", si vous aimez ce mot :) que l'utilisation UPPER(), comme le prouve ce script de test (sur PasteBin) .

    Il y a aussi le problème noté par @Ceisc sur la réponse de @ Danny:

    Dans certaines langues, les conversions de cas ne sont pas aller-retour. c'est-à-dire INFÉRIEUR (x)! = INFÉRIEUR (SUPÉRIEUR (x)).

    La majuscule turque "İ" est l'exemple courant.

  2. Non, le classement n'est pas un paramètre à l'échelle de la base de données, du moins pas dans ce contexte. Il existe un classement par défaut au niveau de la base de données, et il est utilisé par défaut pour les colonnes modifiées et nouvellement créées qui ne spécifient pas la COLLATEclause (ce qui est probablement la source de cette idée fausse commune), mais elle n'a pas d'incidence directe sur les requêtes, sauf si vous êtes comparer des littéraux de chaîne et des variables à d'autres littéraux de chaîne et variables, ou vous faites référence à des métadonnées au niveau de la base de données.

  3. Non, le classement n'est pas par requête.

  4. Les classements sont par prédicat (c'est-à-dire quelque chose d'opérande) ou expression, pas par requête. Et cela est vrai pour l'ensemble de la requête, pas seulement pour la WHEREclause. Cela couvre les JOINs, GROUP BY, ORDER BY, PARTITION BY, etc.

  5. Non, ne pas convertir en VARBINARY(par exemple convert(varbinary, myField) = convert(varbinary, 'sOmeVal')) pour les raisons suivantes:

    1. c'est une comparaison binaire, qui n'est pas insensible à la casse (c'est ce que demande cette question)
    2. si vous voulez une comparaison binaire, utilisez un classement binaire. Utilisez-en un qui se termine par _BIN2si vous utilisez SQL Server 2008 ou plus récent, sinon vous n'avez pas d'autre choix que d'en utiliser un qui se termine par _BIN. Si les données le sont, NVARCHARpeu importe la langue que vous utilisez, car elles sont toutes identiques dans ce cas, elles fonctionnent donc Latin1_General_100_BIN2toujours. Si les données VARCHAR, vous devez utiliser les mêmes paramètres régionaux que les données sont actuellement (par exemple Latin1_General, French, Japanese_XJIS, etc.) parce que les paramètres régionaux détermine la page de code qui est utilisé, et la modification des pages de code peut modifier les données (perte de données).
    3. l'utilisation d'un type de données de longueur variable sans spécifier la taille dépendra de la taille par défaut, et il existe deux valeurs par défaut différentes selon le contexte dans lequel le type de données est utilisé. Il vaut 1 ou 30 pour les types chaîne. Lorsqu'il est utilisé avec, CONVERT()il utilisera la valeur par défaut 30. Le danger est que si la chaîne peut dépasser 30 octets, elle sera silencieusement tronquée et vous obtiendrez probablement des résultats incorrects de ce prédicat.
    4. Même si vous voulez une comparaison sensible à la casse, les classements binaires ne sont pas sensibles à la casse (une autre idée fausse très courante).
  6. Non, LIKEn'est pas toujours sensible à la casse. Il utilise le classement de la colonne référencée, ou le classement de la base de données si une variable est comparée à un littéral de chaîne, ou le classement spécifié via la COLLATEclause facultative .

  7. LCASEn'est pas une fonction SQL Server. Il semble que ce soit Oracle ou MySQL. Ou peut-être Visual Basic?

  8. Étant donné que le contexte de la question compare une colonne à un littéral de chaîne, ni le classement de l'instance (souvent appelé «serveur») ni le classement de la base de données n'ont ici d'impact direct . Les classements sont stockés pour chaque colonne et chaque colonne peut avoir un classement différent, et ces classements n'ont pas besoin d'être les mêmes que le classement par défaut de la base de données ou le classement de l'instance. Bien sûr, le classement des instances est le classement par défaut de ce qu'une base de données nouvellement créée utilisera comme classement par défaut si la COLLATEclause n'a pas été spécifiée lors de la création de la base de données. Et de même, le classement par défaut de la base de données est ce qu'une colonne modifiée ou nouvellement créée utilisera si la COLLATEclause n'a pas été spécifiée.

  9. Vous devez utiliser le classement insensible à la casse qui est sinon le même que le classement de la colonne. Utilisez la requête suivante pour trouver le classement de la colonne (modifiez le nom de la table et le nom du schéma):

    SELECT col.*
    FROM   sys.columns col
    WHERE  col.[object_id] = OBJECT_ID(N'dbo.TableName')
    AND    col.[collation_name] IS NOT NULL;
    

    Ensuite, changez simplement le _CSpour être _CI. Alors, Latin1_General_100_CS_ASdeviendrait Latin1_General_100_CI_AS.

    Si la colonne utilise un classement binaire (se terminant par _BINou _BIN2), recherchez un classement similaire à l'aide de la requête suivante:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'{CurrentCollationMinus"_BIN"}[_]CI[_]%';
    

    Par exemple, en supposant que la colonne utilise Japanese_XJIS_100_BIN2, procédez comme suit:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'Japanese_XJIS_100[_]CI[_]%';
    

Pour plus d' informations sur les classements, codages, etc., visitez s'il vous plaît: collation Infos

Solomon Rutzky
la source
7

Non, seule l'utilisation LIKEne fonctionnera pas. LIKErecherche des valeurs correspondant exactement à votre modèle donné. Dans ce cas LIKE, trouverait uniquement le texte «sOmeVal» et non «someval».

Une solution pratique consiste à utiliser la LCASE()fonction. LCASE('sOmeVal')obtient la chaîne minuscule de votre texte: 'someval'. Si vous utilisez cette fonction pour les deux côtés de votre comparaison, cela fonctionne:

SELECT * FROM myTable WHERE LCASE(myField) LIKE LCASE('sOmeVal')

L'instruction compare deux chaînes minuscules, de sorte que votre 'sOmeVal' correspondra à toutes les autres notations de 'someval' (par exemple 'Someval', 'sOMEVAl' etc.).

David Hermanns
la source
7
Dans 99,9% des installations SQL Server qui sont classées _CI, LIKE est insensible à la casse.
RichardTheKiwi
1
De nos jours, la fonction s'appelle LOWER
David Brossard
@DavidBrossard et David Hermanns, je ne pense pas que cela ait jamais été LCASE()dans SQL Server (du moins pas que je puisse voir). Je pense que cette réponse est pour un SGBDR entièrement différent. Veuillez consulter ma réponse pour obtenir des précisions sur les comparaisons de chaînes.
Solomon Rutzky
4

Vous pouvez forcer la sensibilité à la casse, en effectuant un cast en varbinary comme ça:

SELECT * FROM myTable 
WHERE convert(varbinary, myField) = convert(varbinary, 'sOmeVal')

la source
3
Bien que cela soit fonctionnel, ce n'est pas une approche recommandée. Les classements sont là pour gérer le tri et les comparaisons de chaînes.
Adam Robinson
@AdamRobinson n'est-il pas question de "comparaisons de chaînes"?
Fandango68
@ Fandango68 Oui, c'est vrai, et Adam dit que les classements sont meilleurs quand on fait des comparaisons de chaînes.
JLRishe
@ Fandango68 Cette réponse est fausse à plusieurs niveaux. Veuillez consulter ma réponse pour plus de détails, en particulier le point 5.
Solomon Rutzky
@AdamRobinson Veuillez consulter ma réponse pour obtenir des éclaircissements sur les comparaisons de chaînes.
Solomon Rutzky
2

Sur quelle base de données êtes-vous? Avec MS SQL Server, il s'agit d'un paramètre à l'échelle de la base de données, ou vous pouvez le remplacer par requête avec le mot clé COLLATE.

Chase Seibert
la source
Salut. Pour SQL Server, en ce qui concerne l'objet de cette question, il ne s'agit ni d'un paramètre à l'échelle de la base de données ni d'une requête par requête. Veuillez consulter ma réponse pour plus de détails.
Solomon Rutzky