Mélange illégal de classements (utf8_unicode_ci, IMPLICIT) et (utf8_general_ci, IMPLICIT) pour l'opération '='

160

Message d'erreur sur MySql:

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

J'ai parcouru plusieurs autres articles et je n'ai pas pu résoudre ce problème. La partie affectée est quelque chose de similaire à ceci:

CREATE TABLE users (
    userID INT UNSIGNED NOT NULL AUTO_INCREMENT,
    firstName VARCHAR(24) NOT NULL,
    lastName VARCHAR(24) NOT NULL,
    username VARCHAR(24) NOT NULL,
    password VARCHAR(40) NOT NULL,
    PRIMARY KEY (userid)
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE products (
    productID INT UNSIGNED NOT NULL AUTO_INCREMENT,
    title VARCHAR(104) NOT NULL,
    picturePath VARCHAR(104) NULL,
    pictureThumb VARCHAR(104) NULL,
    creationDate DATE NOT NULL,
    closeDate DATE NULL,
    deleteDate DATE NULL,
    varPath VARCHAR(104) NULL,
    isPublic TINYINT(1) UNSIGNED NOT NULL DEFAULT '1',
    PRIMARY KEY (productID)
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE productUsers (
    productID INT UNSIGNED NOT NULL,
    userID INT UNSIGNED NOT NULL,
    permission VARCHAR(16) NOT NULL,
    PRIMARY KEY (productID,userID),
    FOREIGN KEY (productID) REFERENCES products (productID) ON DELETE RESTRICT ON UPDATE NO ACTION,
    FOREIGN KEY (userID) REFERENCES users (userID) ON DELETE RESTRICT ON UPDATE NO ACTION
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

La procédure stockée que j'utilise est la suivante:

CREATE PROCEDURE updateProductUsers (IN rUsername VARCHAR(24),IN rProductID INT UNSIGNED,IN rPerm VARCHAR(16))
BEGIN
    UPDATE productUsers
        INNER JOIN users
        ON productUsers.userID = users.userID
        SET productUsers.permission = rPerm
        WHERE users.username = rUsername
        AND productUsers.productID = rProductID;
END

Je testais avec php, mais la même erreur est donnée avec SQLyog. J'ai également testé la recréation de l'ensemble de la base de données, mais sans rien de bon.

Toute aide sera très appréciée.

Manatax
la source

Réponses:

220

Le classement par défaut pour les paramètres de procédure stockée est utf8_general_ciet vous ne pouvez pas mélanger les classements, vous avez donc quatre options:

Option 1 : ajoutez COLLATEà votre variable d'entrée:

SET @rUsername = aname COLLATE utf8_unicode_ci; -- COLLATE added
CALL updateProductUsers(@rUsername, @rProductID, @rPerm);

Option 2 : ajouter COLLATEà la WHEREclause:

CREATE PROCEDURE updateProductUsers(
    IN rUsername VARCHAR(24),
    IN rProductID INT UNSIGNED,
    IN rPerm VARCHAR(16))
BEGIN
    UPDATE productUsers
        INNER JOIN users
        ON productUsers.userID = users.userID
        SET productUsers.permission = rPerm
        WHERE users.username = rUsername COLLATE utf8_unicode_ci -- COLLATE added
        AND productUsers.productID = rProductID;
END

Option 3 : ajoutez-le à la INdéfinition du paramètre:

CREATE PROCEDURE updateProductUsers(
    IN rUsername VARCHAR(24) COLLATE utf8_unicode_ci, -- COLLATE added
    IN rProductID INT UNSIGNED,
    IN rPerm VARCHAR(16))
BEGIN
    UPDATE productUsers
        INNER JOIN users
        ON productUsers.userID = users.userID
        SET productUsers.permission = rPerm
        WHERE users.username = rUsername
        AND productUsers.productID = rProductID;
END

Option 4 : modifier le champ lui-même:

ALTER TABLE users CHARACTER SET utf8 COLLATE utf8_general_ci;

À moins que vous n'ayez besoin de trier les données dans l'ordre Unicode, je vous suggère de modifier toutes vos tables pour utiliser le utf8_general_ciclassement, car cela ne nécessite aucun changement de code et accélérera légèrement le tri .

MISE À JOUR : utf8mb4 / utf8mb4_unicode_ci est maintenant la méthode de jeu de caractères / collation préférée. utf8_general_ci est déconseillé, car l'amélioration des performances est négligeable. Voir https://stackoverflow.com/a/766996/1432614

Ross Smith II
la source
1
Il est également possible d'ajouter COLLATE utf8_unicode_cides constantes de chaîne: SET @EMAIL = '[email protected]' COLLATE utf8_unicode_ci;. C'est particulièrement utile si vous exécutez un script à partir d'une console, où l'encodage par défaut de la console s'applique au classement de vos constantes de chaîne.
gaborsch
Ou supprimez la base de données et créez-en une nouvelle avec utf8_general_ci; collation.
Oleksii Kyslytsyn
2
Pour référence future, ne changez pas toutes vos tables en utf8_general_ci à moins que vous ne compreniez les différences entre les deux classements.
Manatax le
1
@GaborSch L'ajout de l'assemblage aux variables de chaîne était la solution pour moi, j'ai écrit une réponse détaillée à ce sujet avant de remarquer votre commentaire.
nkatsar
im obtenir la même erreur, sauf (utf8mb4_unicode_ci, IMPLICIT)au lieu de (utf8_unicode_ci, IMPLICIT). Je racle des données sur le Web en utilisant python, puis je crée un fichier CSV avec les données grattées, que je traite ensuite avec un fichier PHP sur mon serveur qui télécharge les données dans ma base de données. toutes mes tables / colonnes MySQL sont classées comme utf8mb4_unicode_ci. le problème peut-il se produire parce que j'encode les données comme utf8en python / csv?
oldboy
27

J'ai passé une demi-journée à chercher des réponses à une erreur identique "Mélange illégal de collations" avec des conflits entre utf8_unicode_ci et utf8_general_ci.

J'ai trouvé que certaines colonnes de ma base de données n'étaient pas spécifiquement rassemblées utf8_unicode_ci . Il semble que mysql ait implicitement rassemblé ces colonnes utf8_general_ci .

Plus précisément, l'exécution d'une requête «SHOW CREATE TABLE table1» a généré quelque chose comme ce qui suit:

| table1 | CREATE TABLE `table1` (
`id` int(11) NOT NULL,
`col1` varchar(4) CHARACTER SET utf8 NOT NULL,
`col2` int(11) NOT NULL,
PRIMARY KEY (`col1`,`col2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

Notez que la ligne 'col1' varchar (4) CHARACTER SET utf8 NOT NULL n'a pas de classement spécifié. J'ai ensuite exécuté la requête suivante:

ALTER TABLE table1 CHANGE col1 col1 VARCHAR(4) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;

Cela a résolu mon erreur "Mélange illégal de classements". J'espère que cela pourrait aider quelqu'un d'autre.

Nate Vaughan
la source
7
Merci. «SHOW CREATE TABLE» est le moyen le plus simple de comprendre et de résoudre la cause première du problème.
joro
2
Notez également que spécifier COLLATEpour la table entière (ie ALTER TABLE table1 CHARSET utf8 COLLATE utf8_unicode_ci) ne résoudra pas le problème , cela doit être fait pour chaque colonne (problématique).
Skippy le Grand Gourou
6

J'ai eu un problème similaire, mais cela m'est venu à l'esprit dans la procédure, lorsque mon paramètre de requête a été défini à l'aide de la variable, par exemple SET @value='foo'.

La cause de ce problème était incompatible collation_connectionavec le classement de la base de données. Changé collation_connectionpour correspondre collation_databaseet le problème a disparu. Je pense que c'est une approche plus élégante que d'ajouter COLLATE après param / value.

Pour résumer: tous les classements doivent correspondre. Utilisation SHOW VARIABLESet assurez - vous collation_connectionet collation_databasematch (vérifier également la collation de table à l' aide SHOW TABLE STATUS [table_name]).

bpile
la source
1
Le même problème m'est arrivé, j'ai évité de modifier les variables collation_YYY en définissant le classement directement dans la déclaration de variable. SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
nkatsar
5

Un peu similaire à la réponse @bpile, mon cas était un paramètre d'entrée my.cnf collation-server = utf8_general_ci. Après avoir réalisé cela (et après avoir tout essayé ci-dessus), j'ai basculé de force ma base de données sur utf8_general_ci au lieu de utf8_unicode_ci et c'était tout:

ALTER DATABASE `db` CHARACTER SET utf8 COLLATE utf8_general_ci;
Sebas
la source
1
C'est étrange que les configurations soient tellement dispersées. Toutes les valeurs par défaut de classement doivent être définies au même endroit.
Manatax
0

Dans mon propre cas, j'ai l'erreur suivante

Mélange illégal de classements (utf8_general_ci, IMPLICIT) et (utf8_unicode_ci, IMPLICIT) pour l'opération '='

$ this-> db-> select ("users.username as matric_no, CONCAT (users.surname, '', users.first_name, '', users.last_name) as fullname") -> join ('users', 'users .username = class_students.matric_no ',' left ') -> where (' class_students.session_id ', $ session) -> where (' class_students.level_id ', $ level) -> where (' class_students.dept_id ', $ dept );

Après des semaines de recherche sur Google, j'ai remarqué que les deux champs que je compare consistent en un nom de classement différent. Le premier c'est-à-dire le nom d'utilisateur est de utf8_general_ci tandis que le second est de utf8_unicode_ci donc je suis retourné à la structure de la deuxième table et j'ai changé le deuxième champ (matric_no) en utf8_general_ci et cela a fonctionné comme un charme.

Teejaygenius
la source
0

Malgré le fait d'avoir trouvé un nombre énorme de questions sur le même problème ( 1 , 2 , 3 , 4 ), je n'ai jamais trouvé de réponse tenant compte des performances, même ici.

Bien que plusieurs solutions de travail aient déjà été proposées, j'aimerais examiner les performances.

EDIT: Merci à Manatax pour avoir souligné que l'option 1 ne souffre pas de problèmes de performances.

L'utilisation des options 1 et 2 , alias l' approche de conversion COLLATE , peut entraîner un goulot d'étranglement potentiel, car tout index défini sur la colonne ne sera pas utilisé, entraînant une analyse complète .

Même si je n'ai pas essayé l' option 3 , je pense qu'elle subira les mêmes conséquences que l'option 1 et 2.

Enfin, l' option 4 est la meilleure option pour les très grandes tables lorsqu'elle est viable. Je veux dire qu'il n'y a aucune autre utilisation qui repose sur le classement d'origine.

Considérez cette requête simplifiée:

SELECT 
    *
FROM
    schema1.table1 AS T1
        LEFT JOIN
    schema2.table2 AS T2 ON T2.CUI = T1.CUI
WHERE
    T1.cui IN ('C0271662' , 'C2919021')
;

Dans mon exemple d'origine, j'ai eu beaucoup plus de jointures. Bien sûr, table1 et table2 ont des classements différents. Utilisation de l' assemblage opérateur d' pour effectuer un cast, entraînera la non-utilisation des index.

Voir l'explication SQL dans l'image ci-dessous.

Explication de la requête visuelle lors de l'utilisation de la distribution COLLATE

D'autre part, l' option 4 peut tirer parti des avantages de l'index possible et conduit à des requêtes rapides.

Dans l'image ci-dessous, vous pouvez voir la même requête en cours d'exécution après l'application de l' option 4 , c'est-à-dire en modifiant le classement schéma / table / colonne.

Explication de la requête visuelle après la modification du classement, et donc sans le cast de classement

En conclusion, si les performances sont importantes et que vous pouvez modifier le classement du tableau, optez pour l' option 4 . Si vous devez agir sur une seule colonne, vous pouvez utiliser quelque chose comme ceci:

ALTER TABLE schema1.table1 MODIFY `field` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Raffaele
la source
Merci pour votre contribution Raffaele, mais je pense que l'option 1 utiliserait l'index, car vous ne lancez pas la table, mais la valeur de comparaison avant même de la transmettre au SP.
Manatax
Merci d'avoir fait remarquer cela. C'etait mon erreur. J'ai modifié ma réponse en conséquence.
Raffaele
0

Cela se produit lorsqu'une colonne est explicitement définie sur un classement différent ou le classement par défaut est différent dans la table interrogée.

si vous avez de nombreuses tables pour lesquelles vous souhaitez modifier le classement, exécutez cette requête:

select concat('ALTER TABLE ', t.table_name , ' CONVERT TO CHARACTER 
SET utf8 COLLATE utf8_unicode_ci;') from (SELECT table_name FROM 
information_schema.tables where table_schema='SCHRMA') t;

cela produira les requêtes nécessaires pour convertir toutes les tables pour utiliser le classement correct par colonne

raam86
la source
Cela se produit également lorsque (comme dans mon cas) votre classement par défaut pour SP est différent de celui utilisé pour la table interrogée.
Manatax