web-dev-qa-db-fra.com

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

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 postes et je n'ai pas pu résoudre ce problème. La partie touchée ressemble à 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 essayé de recréer la totalité de la base de données, mais sans succès.

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

134
Manatax

Il y a 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 : ajoutez COLLATE à la clause WHERE:

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 définition du paramètre IN:

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 : modifiez le champ lui-même:

ALTER TABLE users CHARACTER SET utf8 COLLATE utf8_general_ci;

le classement par défaut pour les paramètres de procédure stockée est utf8_general_ci et vous ne pouvez pas mélanger les classements.

À moins que vous n'ayez besoin de trier les données dans l'ordre Unicode, je suggérerais de modifier toutes vos tables pour qu'elles utilisent le classement utf8_general_ci, car cela ne nécessite aucune modification de code et accélère légèrement le tri.

UPDATE: utf8mb4/utf8mb4_unicode_ci est maintenant la méthode de jeu de caractères/classement 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

187
Ross Smith II

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 assemblées tf8_unicode_ci. Il semble que mysql ait implicitement rassemblé ces colonnes tf8_general_ci.

En particulier, 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 la ligne 'col1' varchar (4) CHARACTER SET utf8 NOT NULL n'a pas de classement spécifié. J'ai ensuite lancé 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 collations". J'espère que cela pourrait aider quelqu'un d'autre là-bas.

22
Nate Vaughan

J'ai eu un problème similaire, mais il m'est apparu à l'intérieur d'une procédure lorsque mon paramètre de requête a été défini à l'aide de la variable p. Ex. SET @value='foo'.

La cause en était incompatible collation_connection et le classement dans la base de données. Changement de collation_connection pour correspondre à collation_database et le problème a disparu. Je pense que cette approche est plus élégante que d'ajouter COLLATE après param/value.

En résumé: toutes les collations doivent correspondre. Utilisez SHOW VARIABLES et assurez-vous que collation_connection et collation_database correspondent (vérifiez également le classement dans la table à l'aide de SHOW TABLE STATUS [table_name]).

5
bpile

Un peu similaire à @bpile answer, 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 essayé tout ce qui est décrit ci-dessus), j'ai forcé ma base de données à passer à utf8_general_ci au lieu de utf8_unicode_ci et le résultat était le suivant:

ALTER DATABASE `db` CHARACTER SET utf8 COLLATE utf8_general_ci;
4
Sebas

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) comme nom complet") -> join ('users', 'utilisateurs .username = classe_etudiants.matric_no ',' gauche ') -> où (' classe_students.session_id ', $ session) -> où (' classe_students.level_id ', $ niveau) -> où (' classe_students.dept_id ', $ dept )

Après des semaines de recherche sur Google, j'ai remarqué que les deux champs que je compare se composent de différents noms de collation. Le premier nom d'utilisateur est utf8_general_ci et le second utf8_unicode_ci. Je suis donc retourné à la structure de la deuxième table et j'ai changé le second champ (matric_no) en utf8_general_ci.

0
Teejaygenius

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

si vous souhaitez modifier le classement de nombreuses tables, 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 afin d'utiliser le classement correct par colonne

0
raam86

En dépit de trouver un nombre énorme de questions sur le même problème ( 1 , 2 , , 4 ) j'ai jamais trouvé de réponse prenant en compte la performance, même ici.

Bien que plusieurs solutions de travail aient déjà été proposées, je voudrais faire une analyse de performance.

EDIT: Merci à Manatax pour avoir signalé que l’option 1 ne souffrait pas de problèmes de performances.

Utilisation de l'option  1 et 2 , alias l'approche de conversion COLLATE, peut entraîner un goulot d'étranglement potentiel, car aucun index défini sur la colonne ne sera utilisé provoquant une analyse complète .

Même si je n’ai pas essayé l’option , option 3 , j’ai l’intention de subir 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 pas d'autre utilisation qui s'appuie sur le classement d'origine.

Considérons 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 initial, j'avais beaucoup plus de jointures. Bien entendu, table1 et table2 ont des classements différents. En utilisant l'opérateur collate pour le transtypage, les index ne seront pas utilisés.

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

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

D'autre part, l'option 4 peut tirer parti des possibilités d'index et mener à des requêtes rapides.

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

Explication de la requête visuelle après la modification de la classification, et donc sans la distribution de la synthèse

En conclusion, si les performances sont importantes et que vous pouvez modifier le classement de la table, optez pour 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;
0
Raffaele