web-dev-qa-db-fra.com

Dépannage de l'erreur "Mélange illégal de classements" dans mysql

Je reçois le message d'erreur ci-dessous lorsque je tente d'effectuer une sélection via une procédure stockée dans MySQL.

Mélange illégal de collations (latin1_general_cs, IMPLICIT) et (latin1_general_ci, IMPLICIT) pour l'opération '='

Une idée sur ce qui pourrait mal se passer ici?

Le classement de la table est latin1_general_ci et celui de la colonne de la clause where est latin1_general_cs.

177
user355562

Cela est généralement dû à la comparaison de deux chaînes de classement incompatible ou à la tentative de sélectionner des données de classement différent dans une colonne combinée.

La clause COLLATE vous permet de spécifier le classement utilisé dans la requête.

Par exemple, la clause WHERE suivante donnera toujours l'erreur que vous avez publiée:

WHERE 'A' COLLATE latin1_general_ci = 'A' COLLATE latin1_general_cs

Votre solution consiste à spécifier un classement partagé pour les deux colonnes de la requête. Voici un exemple qui utilise la clause COLLATE:

SELECT * FROM table ORDER BY key COLLATE latin1_general_ci;

Une autre option consiste à utiliser l'opérateur BINARY:

BINARY str est le raccourci pour CAST (str AS BINARY).

Votre solution pourrait ressembler à quelque chose comme ça:

SELECT * FROM table WHERE BINARY a = BINARY b;

ou,

SELECT * FROM table ORDER BY BINARY a;
183
defines

TL; DR

Modifiez le classement d'une (ou des deux) des chaînes afin qu'elles correspondent, ou ajoutez une clause COLLATE à votre expression.


  1. Quel est ce "classement" de toute façon?

    Comme indiqué dans la section Jeux de caractères et collations en général :

    Un jeu de caractères est un ensemble de symboles et d'encodages. Un classement est un ensemble de règles permettant de comparer des caractères dans un jeu de caractères. Faisons la distinction avec un exemple de jeu de caractères imaginaire.

    Supposons que nous ayons un alphabet avec quatre lettres: “A”, “B”, “a”, “b”. Nous attribuons à chaque lettre un chiffre: “A” = 0, “B” = 1, “a” = 2, “b” = 3. La lettre “ A ”est un symbole, le nombre 0 est l’encodage pour“ A ”, et la combinaison des quatre lettres et de leurs codages est un jeu de caractères (-) .

    Supposons que nous voulions comparer deux valeurs de chaîne, “A” et “B”. La façon la plus simple de procéder consiste à examiner les codages: 0 pour "A" et 1 pour "B". Parce que 0 est inférieur à 1, on dit “A” est inférieur à “B”. Ce que nous venons de faire, c'est d'appliquer un classement à notre jeu de caractères. La collation est un ensemble de règles (une seule règle dans ce cas): "comparez les codages". Nous appelons cela la plus simple des collations possibles un binaire collation.

    Mais que se passe-t-il si nous voulons dire que les lettres minuscules et majuscules sont équivalentes? Nous aurions alors au moins deux règles: (1) traiter les lettres minuscules "a" et "b" comme équivalentes à "A" et "B"; (2) puis comparez les encodages. Nous appelons cela un classement insensible à la casse . C'est un peu plus complexe qu'un classement binaire.

    Dans la vie réelle, la plupart des jeux de caractères comportent de nombreux caractères: non seulement "A" et "B", mais des alphabets entiers, parfois de multiples alphabets ou des systèmes d'écriture orientaux comportant des milliers de caractères, ainsi que de nombreux symboles et symboles spéciaux. signe de ponctuation. Toujours dans la vie réelle, la plupart des collations ont de nombreuses règles, non seulement pour distinguer le cas du courrier, mais également pour distinguer les accents (un "accent" est une marque attachée à un caractère comme en allemand "_Ö_"), et pour les mappages à plusieurs caractères (tels que la règle selon laquelle "_Ö_" = "OE" dans l'un des deux classements allemands).

    D'autres exemples sont donnés sous Exemples d'effet de collation .

  2. OK, mais comment MySQL décide-t-il quel classement utiliser pour une expression donnée?

    Comme indiqué dans la section Collation d’expressions :

    Dans la grande majorité des déclarations, le classement utilisé par MySQL pour résoudre une opération de comparaison est évident. Par exemple, dans les cas suivants, il doit être clair que le classement est le classement de la colonne _charset_name_:

    _SELECT x FROM T ORDER BY x;
    SELECT x FROM T WHERE x = x;
    SELECT DISTINCT x FROM T;
    _

    Cependant, avec plusieurs opérandes, il peut y avoir une ambiguïté. Par exemple:

    _SELECT x FROM T WHERE x = 'Y';
    _

    La comparaison doit-elle utiliser le classement de la colonne x ou du littéral chaîne _'Y'_? x et _'Y'_ ont tous deux des classements, alors quel classement a priorité?

    Le SQL standard résout de telles questions en utilisant ce que l'on appelait des règles de "coercibilité".

    [ deletia ]

    MySQL utilise des valeurs de coercibilité avec les règles suivantes pour résoudre les ambiguïtés:

    • Utilisez le classement avec la valeur de coercibilité la plus faible.

    • Si les deux côtés ont la même coercibilité, alors:

      • Si les deux côtés sont en Unicode, ou si les deux côtés ne sont pas Unicode, c'est une erreur.

      • Si l'un des côtés possède un jeu de caractères Unicode et qu'un autre possède un jeu de caractères non-Unicode, le côté avec le jeu de caractères Unicode l'emporte et la conversion automatique du jeu de caractères s'applique au côté non Unicode. Par exemple, l'instruction suivante ne renvoie pas d'erreur:

        _SELECT CONCAT(utf8_column, latin1_column) FROM t1;
        _

        Il renvoie un résultat dont le jeu de caractères est _utf8_ et le même classement que _utf8_column_. Les valeurs de _latin1_column_ sont automatiquement converties en _utf8_ avant la concaténation.

      • Pour une opération avec des opérandes du même jeu de caractères mais mélangeant un classement __bin_ et un classement __ci_ ou __cs_, le classement __bin_ est utilisé. Ceci est similaire à la façon dont les opérations qui mélangent des chaînes non binaires et binaires évaluent les opérandes en tant que chaînes binaires, à la différence qu'il s'agit de collations plutôt que de types de données.

  3. Qu'est-ce qu'un "mélange illégal de collations"?

    Un "mélange illégal de collations" se produit lorsqu'une expression compare deux chaînes de collations différentes mais de même coercibilité et que les règles de coercibilité ne peuvent pas aider à résoudre le conflit. C'est la situation décrite sous le troisième point de la citation ci-dessus.

    L'erreur particulière donnée dans la question, Illegal mix of collations (latin1_general_cs,IMPLICIT) and (latin1_general_ci,IMPLICIT) for operation '=', nous indique qu'il y a eu une comparaison d'égalité entre deux chaînes non Unicode de même coercibilité. De plus, cela nous indique que les classements n'étaient pas donnés explicitement dans la déclaration, mais étaient plutôt déduits des sources des chaînes (telles que les métadonnées de colonne).

  4. C'est très bien, mais comment résoudre de telles erreurs?

    Comme le suggèrent les extraits de manuel cités ci-dessus, ce problème peut être résolu de différentes manières, dont deux sont sensibles et doivent être recommandées:

    • Modifiez le classement de l'une des chaînes (ou des deux) afin qu'elles correspondent et qu'il n'y ait plus d'ambiguïté.

      La procédure à suivre dépend de l'origine de la chaîne: les expressions littérales prennent le classement spécifié dans la variable système collation_connection ; les valeurs des tables prennent le classement spécifié dans leurs métadonnées de colonne.

    • Force une chaîne à ne pas être coercible.

      J'ai omis la citation suivante de ce qui précède:

      MySQL assigne les valeurs de coercibilité comme suit:

      • Une clause explicite COLLATE a une coercibilité de 0. (Pas coercible du tout.)

      • La concaténation de deux chaînes avec des collations différentes a une coercibilité de 1.

      • Le classement d'une colonne ou d'un paramètre de routine stocké ou d'une variable locale a une coercibilité de 2.

      • Une "constante système" (la chaîne renvoyée par des fonctions telles que USER() ou VERSION() ) a une coercibilité de 3.

      • La collation d'un littéral a une coercibilité de 4.

      • NULL ou une expression dérivée de NULL a une coercibilité de 5.

      Ainsi, le simple fait d'ajouter une clause COLLATE à l'une des chaînes utilisées dans la comparaison forcera l'utilisation de ce classement.

    Tandis que les autres seraient terriblement une mauvaise pratique s’ils étaient déployés simplement pour résoudre cette erreur:

    • Forcer l'une des chaînes (ou les deux) à avoir une autre valeur de coercibilité afin que l'une d'elles ait la priorité.

      L'utilisation de CONCAT() ou CONCAT_WS() entraînerait une chaîne avec une coercibilité de 1; et (si dans une routine stockée) l'utilisation de paramètres/de variables locales produirait des chaînes avec une coercibilité de 2.

    • Modifiez les encodages de l'une des chaînes (ou des deux) afin que l'un soit Unicode et l'autre non.

      Cela pourrait se faire par transcodage avec CONVERT(expr USING transcoding_name) ; ou en modifiant le jeu de caractères sous-jacent des données (par exemple, modification de la colonne, modification de character_set_connection pour les valeurs littérales, ou envoi de celles-ci à partir du client dans un codage différent et modification de character_set_client /ajouter un introducteur de jeux de caractères). Notez que le changement de codage entraînera d'autres problèmes si certains caractères souhaités ne peuvent pas être codés dans le nouveau jeu de caractères.

    • Modifiez les codages d'une ou des deux chaînes afin qu'elles soient identiques et modifiez une chaîne pour utiliser le classement __bin_ approprié.

      Les méthodes permettant de modifier les codages et les collations ont été détaillées ci-dessus. Cette approche serait peu utile si vous devez appliquer des règles de classement plus avancées que celles proposées par le classement __bin_.

136
eggyal

Ajout de mon 2c à la discussion pour les futurs googlers.

J'étais en train d'enquêter sur un problème similaire dans lequel j'avais l'erreur suivante lors de l'utilisation de functions personnalisé ayant reçu un paramètre varchar:

Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and 
(utf8_general_ci,IMPLICIT) for operation '='

En utilisant la requête suivante:

mysql> show variables like "collation_database";
    +--------------------+-----------------+
    | Variable_name      | Value           |
    +--------------------+-----------------+
    | collation_database | utf8_general_ci |
    +--------------------+-----------------+

J'ai pu constater que la base de données utilisait utf8_general_ci , alors que les tables étaient définies avec utf8_unicode_ci :

mysql> show table status;
    +--------------+-----------------+
    | Name         | Collation       |
    +--------------+-----------------+
    | my_view      | NULL            |
    | my_table     | utf8_unicode_ci |
    ...

Notez que les vues ont un classement NULL. Il semble que les vues et les fonctions aient des définitions de classement même si cette requête indique null pour une vue. Le classement utilisé est le classement de base de données défini lors de la création de la vue/fonction.

La triste solution consistait à la fois à modifier le classement de la base de données et à recréer les vues/fonctions pour les forcer à utiliser le classement actuel.

  • Changer le classement de la base de données:

    ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;
    

J'espère que cela aidera quelqu'un.

52
Ariel T

Parfois, il peut être dangereux de convertir des jeux de caractères, en particulier sur des bases de données contenant d’énormes quantités de données. Je pense que la meilleure option est d'utiliser l'opérateur "binaire":

e.g : WHERE binary table1.column1 = binary table2.column1
13
Justin Vincent

J'avais un problème similaire, j'essayais d'utiliser la procédure FIND_IN_SET avec une chaîne variable .

SET @my_var = 'string1,string2';
SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);

et recevait l'erreur 

Code d'erreur: 1267. Mélange illégal de classements (utf8_unicode_ci, IMPLICIT) et (utf8_general_ci, IMPLICIT) pour l'opération 'find_in_set'

Réponse courte:

Nul besoin de changer les variables collation_YYYY, ajoutez simplement le bon classement à côté de votre déclaration de variable , c.-à-d.

SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);

Longue réponse:

J'ai d'abord vérifié les variables de classement:

mysql> SHOW VARIABLES LIKE 'collation%';
    +----------------------+-----------------+
    | Variable_name        | Value           |
    +----------------------+-----------------+
    | collation_connection | utf8_general_ci |
    +----------------------+-----------------+
    | collation_database   | utf8_general_ci |
    +----------------------+-----------------+
    | collation_server     | utf8_general_ci |
    +----------------------+-----------------+

Puis j'ai vérifié le classement de la table:

mysql> SHOW CREATE TABLE my_table;

CREATE TABLE `my_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `column_name` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=125 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Cela signifie que ma variable a été configurée avec le classement par défaut de utf8_general_ci alors que ma table était configurée comme utf8_unicode_ci .

En ajoutant la commande COLLATE à côté de la déclaration de variable, le classement de variable correspond au classement configuré pour la table.

7
nkatsar

Vous pouvez essayer ce script , qui convertit toutes vos bases de données et tables en utf8.

5
Mirat Can Bayrak

Si les colonnes avec lesquelles vous rencontrez des problèmes sont des "hachages", tenez compte des points suivants ...

Si le "hash" est une chaîne binaire, vous devriez vraiment utiliser le type de données BINARY(...).

Si le "hash" est une chaîne hexagonale, vous n'avez pas besoin de utf8, vous devriez éviter de le faire à cause des contrôles de caractère, etc. Par exemple, MD5(...) de MySQL génère une chaîne hexagonale de 32 octets de longueur fixe. SHA1(...) donne une chaîne hexagonale de 40 octets. Cela pourrait être stocké dans CHAR(32) CHARACTER SET ascii (ou 40 pour sha1).

Ou, mieux encore, enregistrez UNHEX(MD5(...)) dans BINARY(16). Cela réduit de moitié la taille de la colonne. (Cela le rend toutefois plutôt non imprimable.) SELECT HEX(hash) ... si vous voulez que ce soit lisible.

La comparaison de deux colonnes BINARY ne pose aucun problème de classement.

1
Rick James

MySQL n'aime pas vraiment mélanger des classements à moins de pouvoir les contraindre au même (ce qui n'est clairement pas faisable dans votre cas). Ne pouvez-vous pas simplement forcer le même classement à être utilisé via une clause COLLATE ? (ou le raccourci BINARY plus simple si applicable ...).

1
Alex Martelli

Solution si les littéraux sont impliqués.

J'utilise Pentaho Data Integration et je ne peux pas spécifier la syntaxe SQL . L'utilisation d'une recherche de base de données très simple a donné l'erreur "Mélange illégal de collations (cp850_general_ci, COERCIBLE) et (latin1_swedish_ci, COERCIBLE) pour l'opération '= '"

Le code généré était "SELECT DATA_DATE AS latest_DATA_DATE FROM hr_cc_normalised_data_date_v WHERE PSEUDO_KEY =?"

Pour raccourcir l’histoire, la recherche a été faite dans une vue et quand j’ai publié 

mysql> show full columns from hr_cc_normalised_data_date_v;
+------------+------------+-------------------+------+-----+
| Field      | Type       | Collation         | Null | Key |
+------------+------------+-------------------+------+-----+
| PSEUDO_KEY | varchar(1) | cp850_general_ci  | NO   |     |
| DATA_DATE  | varchar(8) | latin1_general_cs | YES  |     |
+------------+------------+-------------------+------+-----+

ce qui explique l'origine de 'cp850_general_ci'.

La vue a été simplement créée avec 'SELECT' X ', ......'.____. Selon les littéraux manuels de ce type, ceux-ci devraient hériter de leur jeu de caractères et de leur classement des paramètres de serveur correctement définis en tant que' latin1 'et' latin1_general_cs. ' comme cela n'est clairement pas arrivé, je l'ai forcé à créer le point de vue

CREATE OR REPLACE VIEW hr_cc_normalised_data_date_v AS
SELECT convert('X' using latin1) COLLATE latin1_general_cs        AS PSEUDO_KEY
    ,  DATA_DATE
FROM HR_COSTCENTRE_NORMALISED_mV
LIMIT 1;

maintenant, il affiche latin1_general_cs pour les deux colonnes et l'erreur a disparu. :)

1
jc508

Une autre source du problème avec les classements est la table mysql.proc. Vérifiez les classements de vos procédures et fonctions de stockage:

SELECT
  p.db, p.db_collation, p.type, COUNT(*) cnt
FROM mysql.proc p
GROUP BY p.db, p.db_collation, p.type;

Faites également attention aux colonnes mysql.proc.collation_connection et mysql.proc.character_set_client.

0
ruvim

Une solution possible consiste à convertir la totalité de la base de données au format UTF8 (voir également cette question ).

0
utapyngo

Si vous avez installé phpMyAdmin, vous pouvez suivre les instructions données dans le lien suivant: https://mediatemple.net/community/products/dv/204403914/default-mysql-character-set-and-collation = Vous devez faire correspondre l'assemblage de la base de données avec celui de toutes les tables, ainsi que les champs des tables, puis recompiler toutes les procédures et fonctions stockées. Avec cela tout devrait fonctionner à nouveau.