web-dev-qa-db-fra.com

Le serveur SQL ignore la casse dans une expression where

Comment construire une requête SQL (MS SQL Server) où la clause "where" ne respecte pas la casse?

SELECT * FROM myTable WHERE myField = 'sOmeVal'

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

78
Raul Agrait

Dans la configuration par défaut d'une base de données SQL Server, les comparaisons de chaînes sont insensibles à la casse . Si votre base de données remplace ce paramètre (par le biais d'un classement alternatif), 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 la collation que j'ai fournie n'est qu'un exemple (bien qu'elle fonctionne probablement très bien pour vous). Vous trouverez un aperçu plus détaillé des classements SQL Server here .

121
Adam Robinson

Habituellement, les comparaisons de chaînes ne sont pas sensibles à la casse. Si votre base de données est configurée pour le classement sensible à la casse, vous devez forcer l'utilisation d'un nom ne respectant pas la casse:

SELECT balance FROM people WHERE email = '[email protected]'
  COLLATE SQL_Latin1_General_CP1_CI_AS 
28
Andrejs Cainikovs

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

upper(@yourString)

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

20
Danny

Les 2 meilleures réponses (de Adam Robinson et Andrejs Cainikovs ) sont un peu correctes, dans la mesure où elles fonctionnent techniquement, mais leurs explications sont erronées et peuvent donc être trompeuses. de nombreux cas. Par exemple, bien que le classement SQL_Latin1_General_CP1_CI_AS Fonctionne dans de nombreux cas, il ne faut pas en déduire qu'il s'agit du classement ne tenant pas compte de la casse. En fait, étant donné que l’OP travaille dans une base de données avec un classement sensible à la casse (ou éventuellement binaire), nous savons qu’il n’utilise pas le classement qui est le paramètre par défaut pour de nombreuses installations (en particulier celles installées sur un système d’exploitation). 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 avec VARCHAR data, il est important de ne pas modifier la page de code, car elle pourrait entraîner une perte de données et est contrôlée par les paramètres régionaux/la culture de la collation (par exemple, Latin1_Général vs Français vs Hébreu, etc.). Veuillez vous reporter au 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. N'utilisez pas UPPER(). C'est un travail supplémentaire totalement inutile. Utilisez une clause COLLATE. Une comparaison de chaîne doit être effectuée dans les deux cas, mais l'utilisation de UPPER() doit également vérifier, caractère par caractère, pour voir s'il existe un mappage en majuscule, puis le modifier. Et vous devez le faire des deux côtés. L'ajout de COLLATE demande simplement au traitement de générer les clés de tri à l'aide d'un ensemble de règles différent de celui utilisé par défaut. Utiliser COLLATE est nettement plus efficace (ou "performant", si vous aimez ce mot :) que d'utiliser UPPER(), comme le prouve ce script de test (sur Pastebin) =.

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

    Dans certaines langues, les conversions ne se font pas. i.e. LOWER (x)! = LOWER (UPPER (x)).

    Les majuscules turques "İ" en sont l'exemple typique.

  2. Non, le classement n'est pas un paramètre à l'échelle de la base de données, du moins dans ce contexte. Il existe un classement par défaut au niveau de la base de données, qui est utilisé par défaut pour les colonnes modifiées et nouvellement créées qui ne spécifient pas la clause COLLATE (ce qui est probablement l’origine de cette idée fausse courante), mais cela n’est pas le cas. impact direct des requêtes, sauf si vous comparez des littéraux de chaîne et des variables avec d'autres littéraux de chaîne et des variables, ou si vous référencez des métadonnées au niveau de la base de données.

  3. Non, le classement ne se fait pas par requête.

  4. Les classements sont par prédicat (c'est-à-dire quelque chose d'opérande) ou d'expression, et non par requête. Et ceci est vrai pour toute la requête, pas seulement pour la clause WHERE. Cela couvre les jointures, GROUP BY, ORDER BY, PARTITION BY, etc.

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

    1. c'est une comparaison binaire, qui n'est pas sensible à la casse (c'est ce que cette question demande)
    2. si vous voulez une comparaison binaire, utilisez un classement binaire. Utilisez celui qui se termine par _BIN2 Si vous utilisez SQL Server 2008 ou une version plus récente, sinon vous n’avez pas d’autre choix que d’en utiliser un qui se termine par _BIN. Si les données sont NVARCHAR alors peu importe la langue que vous utilisez car elles sont toutes identiques dans ce cas, donc Latin1_General_100_BIN2 Fonctionne toujours. Si les données sont VARCHAR, vous devez utiliser les mêmes paramètres régionaux que ceux dans lesquels se trouvent les données (par exemple, Latin1_General, French, Japanese_XJIS, Etc.), car Les paramètres régionaux déterminent la page de code utilisée et le changement de page 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 de taille dépendra de la taille par défaut. Il existe deux valeurs par défaut différentes en fonction du contexte d'utilisation du type de données. C'est 1 ou 30 pour les types de chaîne. Utilisé avec CONVERT(), il utilisera la valeur 30 par défaut. Le danger est que, si la chaîne peut contenir plus de 30 octets, elle sera tronquée de manière silencieuse et vous obtiendrez probablement des résultats incorrects de ce prédicat.
    4. Même si vous souhaitez une comparaison sensible à la casse, les classements binaires ne sont pas sensibles à la casse (une autre idée fausse très courante).
  6. Non, LIKE n'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 clause facultative COLLATE.

  7. LCASE n'est pas une fonction SQL Server. Il semble que ce soit Oracle ou MySQL. Ou éventuellement 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 aucun direct impact ici. Les classements sont stockés pour chaque colonne et chaque colonne peut avoir un classement différent. Ces classements n'ont pas besoin d'être identiques au classement par défaut de la base de données ou au classement de l'instance. Bien sûr, le classement d'instance est la valeur par défaut pour ce qu'une base de données nouvellement créée utilisera comme classement par défaut si la clause COLLATE n'a pas été spécifiée lors de la création de la base de données. De même, le classement par défaut de la base de données correspond à ce qu'une colonne modifiée ou nouvellement créée utilisera si la clause COLLATE n'a pas été spécifiée.

  9. Vous devez utiliser le classement qui ne respecte pas la casse et qui est par ailleurs identique au classement de la colonne. Utilisez la requête suivante pour trouver le classement de la colonne (changez 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;
    

    Puis changez simplement le _CS En _CI. Ainsi, Latin1_General_100_CS_AS Deviendrait Latin1_General_100_CI_AS.

    Si la colonne utilise un classement binaire (se terminant par _BIN Ou _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, les encodages, etc., consultez la page suivante: Infos sur les collations

7
Solomon Rutzky

Non, utiliser seulement LIKE ne fonctionnera pas. LIKE recherche les valeurs correspondant exactement à votre modèle. Dans ce cas, LIKE ne trouverait que le texte "sOmeVal" et non "someval".

Une solution pracitcable utilise la fonction LCASE(). LCASE('sOmeVal') récupère la chaîne en 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" corresponde à toutes les autres notations de "someval" (par exemple, "Someval", "sOMEVAl", etc.).

7
David Hermanns

Vous pouvez forcer le caractère sensible à la casse, en utilisant un varbinary comme celui-ci:

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

Sur quelle base de données es-tu? Avec MS SQL Server, il s'agit d'un paramètre couvrant l'ensemble de la base de données. Vous pouvez également le remplacer par requête à l'aide du mot clé COLLATE.

2
Chase Seibert