操作'='的排序规则(utf8_unicode_ci,IMPLICIT)和(utf8_general_ci,IMPLICIT)的非法混合


160

MySql上的错误消息:

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

我浏览了其他几篇文章,但无法解决此问题。受影响的部分与此类似:

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;

我正在使用的存储过程是这样的:

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

我在用php测试,但是SQLyog给出了相同的错误。我还测试了重新创建整个数据库,但效果不佳。

任何帮助都感激不尽。

Answers:


220

存储过程参数的默认排序规则为utf8_general_ci并且您不能混合排序规则,因此有四个选项:

选项1:添加COLLATE到您的输入变量中:

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

选项2COLLATEWHERE条款中添加:

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

选项3:将其添加到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

选项4:更改字段本身:

ALTER TABLE users CHARACTER SET utf8 COLLATE utf8_general_ci;

除非您需要按Unicode顺序对数据进行排序,否则我建议您更改所有表以使用utf8_general_ci排序规则,因为它不需要更改代码,并且会稍微加快排序速度。

更新:utf8mb4 / utf8mb4_unicode_ci现在是首选的字符集/排序规则方法。建议不要使用utf8_general_ci,因为性能提升可忽略不计。参见https://stackoverflow.com/a/766996/1432614


1
也可以添加COLLATE utf8_unicode_ci到字符串常量:SET @EMAIL = 'abc@def.com' COLLATE utf8_unicode_ci;。如果从控制台运行脚本,则该命令特别有用,在控制台中,控制台默认编码适用于字符串常量的排序规则。
gaborsch '16

或删除数据库并使用utf8_general_ci创建新数据库;整理。
Oleksii Kyslytsyn

2
为了将来参考,除非您了解两个排序规则之间的区别,否则不要将所有表都更改为utf8_general_ci。
Manatax

1
@GaborSch对字符串变量添加排序规则是我的解决方案,在注意到您的评论之前,我写了一个详细的答案。
nkatsar

我收到了相同的错误,但(utf8mb4_unicode_ci, IMPLICIT)不是(utf8_unicode_ci, IMPLICIT)。我正在使用python从网络上抓取数据,然后使用抓取的数据创建CSV文件,然后使用服务器上的PHP文件处理该数据,并将数据上传到我的数据库。我所有的MySQL表/列都整理为utf8mb4_unicode_ci。可能是由于我将数据编码为utf8python / csv中的内容而引起的问题?
老男孩

27

我花了半天的时间寻找与utf8_unicode_ci和utf8_general_ci之间的冲突相同的“排序规则的非法混合”错误的答案。

我发现数据库中的某些列未特别整理utf8_unicode_ci。看来mysql隐式整理了这些列utf8_general_ci

具体来说,运行“ SHOW CREATE TABLE table1”查询将输出类似以下内容:

| 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 |

请注意,'col1'行varchar(4)CHARACTER SET utf8 NOT NULL没有指定排序规则。然后,我运行以下查询:

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

这解决了我的“非法排序规则混合”错误。希望这可以帮助其他人。


7
谢谢。“ SHOW CREATE TABLE”是了解和修复问题根源的最简单方法。
joro

2
还要注意,COLLATE为整个表指定(即ALTER TABLE table1 CHARSET utf8 COLLATE utf8_unicode_ci并不能解决问题,它必须针对每个(问题)列进行。
Skippy le Grand Gourou

6

我有一个类似的问题,但是当我使用变量eg设置查询参数时,它在过程内部发生了SET @value='foo'

导致此问题的原因collation_connection与数据库排序规则不匹配。更改collation_connection为匹配collation_database,问题消失了。我认为这比在param / value之后添加COLLATE更为优雅。

总结:所有归类必须匹配。使用SHOW VARIABLES并确保collation_connectioncollation_database匹配(也使用来检查表排序规则SHOW TABLE STATUS [table_name])。


1
我也遇到了同样的问题,我避免通过直接在变量声明中设置排序规则来避免更改collat​​ion_YYY变量。 SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
nkatsar

5

有点类似于@bpile答案,我的情况是my.cnf输入设置collation-server = utf8_general_ci。在意识到这一点(并尝试了上述所有方法)之后,我将数据库强制切换到utf8_general_ci而不是utf8_unicode_ci,仅此而已:

ALTER DATABASE `db` CHARACTER SET utf8 COLLATE utf8_general_ci;

1
奇怪的是,这些配置分散了这么多。所有排序规则默认值应设置在同一位置。
Manatax '16

0

就我自己而言,我有以下错误

操作'='的排序规则(utf8_general_ci,IMPLICIT)和(utf8_unicode_ci,IMPLICIT)的非法混合

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

经过数周的Google搜索,我注意到我正在比较的两个字段包含不同的归类名称。第一个,即用户名是utf8_general_ci,而第二个是utf8_unicode_ci,所以我回到第二个表的结构,并将第二个字段(matric_no)更改为utf8_general_ci,它像一个魅力一样工作。


0

尽管找到差不多的问题问题的一个巨大的数字(1234)我从来没有发现,把性能考虑,甚至在这里回答。

尽管已经给出了多种可行的解决方案,但我还是要考虑性能。

编辑:感谢Manatax指出选项1不会出现性能问题。

使用选项1和 2,也就是COLLATE强制转换方法,可能会导致潜在的瓶颈,因为将不会使用列上定义的任何索引,从而导致完全扫描

即使我没有尝试过选项3,但我的直觉是它将遭受选项1和 2 的相同后果。

最后,在可行的情况下,选项4是超大型表的最佳选择。我的意思是没有其他用法依赖于原始排序规则。

考虑以下简化查询:

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

在我最初的示例中,我有更多的联接。当然,表1和表2具有不同的排序规则。使用collat​​e运算符进行转换,将导致索引不被使用。

请参见下图中的sql说明。

使用COLLATE强制转换时的可视查询说明

另一方面,选项4可以利用可能的索引并导致快速查询。

在下面的图片中,您可以看到在应用选项4后又运行了相同的查询,也就是更改了架构/表/列排序规则。

排序规则更改后的可视查询说明,因此无需排序规则转换

总之,如果性能很重要,并且您可以更改表的排序规则,请选择选项4。如果您必须对单个列执行操作,则可以使用以下方法:

ALTER TABLE schema1.table1 MODIFY `field` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

感谢您为Raffaele所做的贡献,但是我确实相信选项1将使用索引,因为您不是在转换表,而是在将值传递给SP之前进行比较。
Manatax

感谢您指出了这一点。是我的错 我相应地编辑了答案。
拉斐尔18'Oct

0

在将列显式设置为其他排序规则或查询的表中默认排序规则不同的情况下,会发生这种情况。

如果您有很多表,则要在运行此查询时更改排序规则:

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;

这将输出转换所有表以使用每列正确的排序规则所需的查询


当(例如我的情况)您的SP默认排序规则与查询表所使用的排序规则不同时,也会发生这种情况。
Manatax
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.