web-dev-qa-db-fra.com

Comment puis-je faire une comparaison de chaîne sensible à la casse SQL sur MySQL?

J'ai une fonction qui renvoie cinq caractères avec casse mixte. Si je fais une requête sur cette chaîne, la valeur sera renvoyée indépendamment de la casse.

Comment rendre les requêtes de chaînes MySQL sensibles à la casse?

252
StevenB

http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html

Le jeu de caractères et le classement par défaut sont latin1 et latin1_swedish_ci. Par conséquent, les comparaisons de chaînes non binaires ne respectent pas la casse. Cela signifie que si vous recherchez avec col_name LIKE 'a%', vous obtenez toutes les valeurs de colonne commençant par A ou a. Pour rendre cette recherche sensible à la casse, assurez-vous que l'un des opérandes a un classement sensible à la casse ou binaire. Par exemple, si vous comparez une colonne et une chaîne contenant le jeu de caractères latin1, vous pouvez utiliser l'opérateur COLLATE pour que l'un ou l'autre des opérandes ait le classement latin1_general_cs ou latin1_bin:

col_name COLLATE latin1_general_cs LIKE 'a%'
col_name LIKE 'a%' COLLATE latin1_general_cs
col_name COLLATE latin1_bin LIKE 'a%'
col_name LIKE 'a%' COLLATE latin1_bin

Si vous souhaitez qu'une colonne soit toujours traitée en respectant la casse, déclarez-la avec un classement sensible à la casse ou binaire.

139
drudge

La bonne nouvelle est que si vous devez faire une requête sensible à la casse, il est très facile de le faire:

SELECT *  FROM `table` WHERE BINARY `column` = 'value'
659
Craig White

Au lieu d'utiliser l'opérateur =, vous pouvez utiliser LIKE ou LIKE BINARY

// this returns 1 (true)
select 'A' like 'a'

// this returns 0 (false)
select 'A' like binary 'a'


select * from user where username like binary 'a'

Il faudra "un" et non "un" dans son état

35
insoftservice

Répondre posté Craig White, a une grosse pénalité de performance

SELECT *  FROM `table` WHERE BINARY `column` = 'value'

parce qu'il n'utilise pas d'index. Donc, vous devez soit changer le classement de la table, comme mentionné ici https://dev.mysql.com/doc/refman/5.7/fr/case-sensitivity.html .

OR

Solution la plus simple, vous devriez utiliser un BINARY de valeur.

SELECT *  FROM `table` WHERE `column` = BINARY 'value'

Par exemple.

mysql> EXPLAIN SELECT * FROM temp1 WHERE BINARY col1 = "ABC" AND col2 = "DEF" ;
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | temp1  | ALL  | NULL          | NULL | NULL    | NULL | 190543 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+

CONTRE

mysql> EXPLAIN SELECT * FROM temp1 WHERE col1 = BINARY "ABC" AND col2 = "DEF" ;
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
| id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra                              |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
|  1 | SIMPLE      | temp1 | range | col1_2e9e898e | col1_2e9e898e | 93      | NULL |    2 | Using index condition; Using where |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
enter code here

1 ligne dans le set (0.00 sec)

35
Nitesh

Pour utiliser un index avant d’utiliser BINARY, vous pouvez procéder de la sorte si vous avez de grandes tables.

SELECT
   *
FROM
   (SELECT * FROM `table` WHERE `column` = 'value') as firstresult
WHERE
   BINARY `column` = 'value'

La sous-requête créerait un très petit sous-ensemble insensible à la casse dont vous sélectionneriez ensuite la seule correspondance sensible à la casse.

13
Eric

Ce qui suit est pour les versions de MySQL égales ou supérieures à 5.5.

Ajouter à /etc/mysql/my.cnf

  [mysqld]
  ...
  character-set-server=utf8
  collation-server=utf8_bin
  ...

Toutes les autres collations que j'ai essayées semblaient ne pas être sensibles à la casse, seul "utf8_bin" fonctionnait.

N'oubliez pas de redémarrer mysql après ceci:

   Sudo service mysql restart

Selon http://dev.mysql.com/doc/refman/5.0/fr/case-sensitivity.html , il existe également un "latin1_bin".

Le "utf8_general_cs" n'a pas été accepté par le démarrage de mysql. (Je lis "_cs" comme "sensible à la casse" - ???).

7
fritzthecat

Vous pouvez utiliser BINARY pour tenir compte de la casse, comme ceci

select * from tb_app where BINARY Android_package='com.Mtime';

malheureusement, ce sql ne peut pas utiliser l'index, vous souffrirez d'un impact négatif sur les requêtes qui dépendent de cet index

mysql> explain select * from tb_app where BINARY Android_package='com.Mtime';
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | tb_app | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 1590351 |   100.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+

Heureusement, j'ai quelques astuces pour résoudre ce problème

mysql> explain select * from tb_app where Android_package='com.Mtime' and BINARY Android_package='com.Mtime';
+----+-------------+--------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table  | partitions | type | possible_keys             | key                       | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+--------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | tb_app | NULL       | ref  | idx_Android_pkg           | idx_Android_pkg           | 771     | const |    1 |   100.00 | Using index condition |
+----+-------------+--------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-----------------------+  
5
xiezefan

Le moyen le plus correct d'effectuer une comparaison de chaîne sensible à la casse sans modifier le classement de la colonne interrogée consiste à spécifier explicitement un jeu de caractères et un classement pour la valeur à laquelle la colonne est comparée.

select * from `table` where `column` = convert('value' using utf8mb4) collate utf8mb4_bin;

Pourquoi ne pas utiliser binary?

L'utilisation de l'opérateur binary est déconseillée car elle compare les octets réels des chaînes codées. Si vous comparez les octets réels de deux chaînes codées à l'aide des jeux de caractères différents, deux chaînes devant être considérées comme identiques, peuvent ne pas être identiques. Par exemple, si vous avez une colonne qui utilise le jeu de caractères latin1 et que votre jeu de caractères serveur/session est utf8mb4, alors, lorsque vous comparez la colonne à une chaîne contenant un accent tel que "café", ne correspondra pas aux lignes contenant cette même chaîne! En effet, dans latin1 é est codé sous la forme de l'octet 0xE9 mais dans utf8, il s'agit de deux octets: 0xC3A9.

Pourquoi utiliser convert ainsi que collate?

Les classements doivent correspondre au jeu de caractères. Ainsi, si votre serveur ou votre session est configuré pour utiliser le jeu de caractères latin1, vous devez utiliser collate latin1_bin mais si votre jeu de caractères est utf8mb4, vous devez utiliser collate utf8mb4_bin. Par conséquent, la solution la plus robuste consiste à toujours convertir la valeur en jeu de caractères le plus flexible et à utiliser le classement binaire pour ce jeu de caractères.

Pourquoi appliquer les variables convert et collate à la valeur et non à la colonne?

Lorsque vous appliquez une fonction de transformation à une colonne avant d'effectuer une comparaison, cela empêche le moteur de requête d'utiliser un index s'il en existe un pour la colonne, ce qui pourrait considérablement ralentir votre requête. Par conséquent, il est toujours préférable de transformer la valeur à la place, dans la mesure du possible. Lorsqu'une comparaison est effectuée entre deux valeurs de chaîne et que l'une d'entre elles comporte un classement explicitement spécifié, le moteur de requête utilise le classement explicite, quelle que soit la valeur à laquelle il est appliqué.

Sensibilité d'accent

Il est important de noter que MySql n'est pas seulement sensible à la casse pour les colonnes utilisant un classement _ci (qui est généralement la valeur par défaut), mais aussi accent insensible. Cela signifie que 'é' = 'e'. L'utilisation d'un classement binaire (ou de l'opérateur binary) rendra les comparaisons de chaînes sensibles aux accents, ainsi qu'à la casse.

Qu'est-ce que utf8mb4?

Le jeu de caractères utf8 dans MySql est un alias pour utf8mb3 qui a été obsolète dans les versions récentes car il ne prend pas en charge les caractères à 4 octets (ce qui est important pour l'encodage de chaînes comme? ???) Si vous souhaitez utiliser le encodage de caractères UTF8 avec MySql, utilisez le jeu de caractères utf8mb4.

3
Paul Wheeler

Pas besoin de changer quoi que ce soit au niveau de la base de données, il vous suffit de modifier SQL Query pour que cela fonctionne.

Exemple -

"SELECT * FROM <TABLE> where userId = '" + iv_userId + "' AND password = BINARY '" + iv_password + "'";

Le mot-clé binaire rendra la casse sensible.

2
Pappu Mehta

Excellent!

Je partage avec vous, le code d'une fonction qui compare les mots de passe:

SET pSignal =
(SELECT DECODE(r.usignal,'YOURSTRINGKEY') FROM rsw_uds r WHERE r.uname =
in_usdname AND r.uvige = 1);

SET pSuccess =(SELECT in_usdsignal LIKE BINARY pSignal);

IF pSuccess = 1 THEN
      /*Your code if match*/
ELSE
      /*Your code if don't match*/

END IF;
1
Victor Enrique

mysql n'est pas sensible à la casse par défaut, essayez de changer le classement de langue en latin1_general_cs

0
ohmusama