Mysql
 sql >> Base de données >  >> RDS >> Mysql

MySQL Mélange illégal de classements

Il est utile de comprendre les définitions suivantes :

  • Un encodage de caractères détaille comment chaque symbole est représenté en binaire (et donc stocké dans l'ordinateur). Par exemple, le symbole é (U+00E9, lettre minuscule latine E avec aigu) est encodé comme 0xc3a9 dans UTF-8 (que MySQL appelle utf8 ) et 0xe9 dans Windows-1252 (que MySQL appelle latin1 ).

  • Un jeu de caractères est l'alphabet des symboles qui peuvent être représentés à l'aide d'un codage de caractères donné. De manière confuse, le terme est également utilisé pour signifier la même chose que l'encodage de caractères.

  • Une collation est un ordre sur un jeu de caractères, de sorte que les chaînes puissent être comparées. Par exemple :MySQL latin1_swedish_ci le classement traite la plupart des variations accentuées d'un caractère comme équivalentes au caractère de base, alors que son latin1_general_ci le classement les classera avant le caractère de base suivant mais pas équivalent (il existe également d'autres différences plus importantes :comme l'ordre des caractères comme å , ä , ö et ß ).

MySQL décidera quel classement doit être appliqué à une expression donnée comme documenté sous Assemblage d'expressions :en particulier, la collation d'une colonne est prioritaire sur celle d'un littéral de chaîne.

Le WHERE clause de votre requête compare les chaînes suivantes :

  1. une valeur dans fos_user.username , codé dans le jeu de caractères de la colonne (Windows-1252) et exprimant une préférence pour son classement latin1_swedish_ci (avec une valeur de coercibilité de 2); avec

  2. la chaîne littérale 'Nrv⧧Kasi' , codé dans le jeu de caractères de la connexion (UTF-8, tel que configuré par Doctrine) et exprimant une préférence pour le classement de la connexion utf8_general_ci (avec une valeur de coercibilité de 4).

Étant donné que la première de ces chaînes a une valeur de coercibilité inférieure à la seconde, MySQL tente d'effectuer la comparaison en utilisant le classement de cette chaîne :latin1_swedish_ci . Pour ce faire, MySQL tente de convertir la deuxième chaîne en latin1 —mais depuis le caractère n'existe pas dans ce jeu de caractères, la comparaison échoue.

Avertissement

Il faut s'arrêter un instant pour considérer comment la colonne est actuellement encodée :vous essayez de filtrer les enregistrements où fos_user.username est égal à une chaîne qui contient un caractère qui ne peut pas existent dans cette colonne !

Si vous pensez que la colonne fait contiennent de tels caractères, alors vous avez probablement écrit dans la colonne alors que l'encodage des caractères de connexion était défini sur quelque chose (par exemple, latin1 ) qui a amené MySQL à interpréter la séquence d'octets reçue comme des caractères qui sont tous dans le jeu de caractères Windows-1252.

Si tel est le cas, avant de continuer, corrigez vos données !

  1. convertissez ces colonnes dans l'encodage de caractères utilisé lors de l'insertion des données, s'il est différent de l'encodage en place :

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo;
    
  2. supprimer les informations d'encodage associées à ces colonnes en les convertissant en binary jeu de caractères :

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary;
    
  3. associez à ces colonnes l'encodage dans lequel les données ont été effectivement transmises en les convertissant dans le jeu de caractères approprié.

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET bar;
    

Notez que, si vous convertissez à partir d'un encodage multi-octets, vous devrez peut-être augmenter la taille de la colonne (ou même changer son type) afin d'adapter la longueur maximale possible de la chaîne convertie.

Une fois que l'on est certain que les colonnes sont correctement encodées, on peut forcer la comparaison à être effectuée à l'aide d'un classement Unicode soit—

  • convertir explicitement la valeur fos_user.username à un jeu de caractères Unicode :

    WHERE CONVERT(fos_user.username USING utf8) = ?
    
  • forcer le littéral de chaîne à avoir une valeur de coercibilité inférieure à celle de la colonne (provoquera une conversion implicite de la valeur de la colonne en UTF-8) :

    WHERE fos_user.username = ? COLLATE utf8_general_ci
    

Ou on pourrait, comme vous le dites, convertir définitivement la ou les colonnes en codage Unicode et définir son classement de manière appropriée.

La principale considération est que les encodages Unicode occupent plus d'espace que les jeux de caractères à un octet, donc :

  • plus de stockage peut être nécessaire ;

  • les comparaisons peuvent être plus lentes; et

  • les longueurs de préfixe d'index peuvent devoir être ajustées (notez que le maximum est en octets, donc peut représenter moins de caractères qu'auparavant).

Sachez également que, comme documenté sous ALTER TABLE Syntaxe :