一、问题背景
执行关联查询时,有时会遇到下面的错误:
SELECT s.* FROM sg_corporatesign s JOIN pj_billsignrecord d ON d.signFullName = s.name AND d.signStatus = 2;1267 - Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_unicode_ci,IMPLICIT) for operation '='
错误的意思是:参与等值比较的两个字符串使用了不同的排序规则,MySQL 无法为这次比较自动确定采用哪一个规则。
二、问题分析
1、都用了 utf8mb4,为什么还会报错?
字符集和排序规则是两个不同的概念:
| 概念 | 解决的问题 | 示例 |
|---|---|---|
| 字符集(Character Set) | 字符如何编码、可以表示哪些字符 | utf8mb4 |
| 排序规则(Collation) | 字符串如何比较大小、判断相等和排序 | utf8mb4_unicode_ci |
两个字段都能存中文,并不意味着它们采用相同的比较规则。
本次错误涉及的规则中,signFullName使用的是utf8mb4_0900_ai_ci排序规则,而utf8mb4_0900_ai_ci基于 Unicode 9.0 的排序算法,ai表示不区分重音,ci表示不区分大小写;name使用的是utf8mb4_unicode_ci排序规则,utf8mb4_unicode_ci是较早的 Unicode 排序规则,也不区分大小写。它们不能简单地视为同一个规则的两个名字。一旦两个关联字段的排序规则不一致的话,就可能产生本文出现的错误。
2、问题通常是怎么出现的?
常见原因是表结构经过多次演变,相关字段的定义逐渐不一致:
- 新建表采用了
utf8mb4_0900_ai_ci,旧表保留utf8mb4_unicode_ci,关联时发生冲突。 - 导入外部数据表、创建临时表时,采用了不同的默认规则。
- 跨库查询涉及的字段分别继承了各自数据库或表的默认配置。
3、解决方案
(1)、在查询中显式指定 COLLATE
当暂时不能修改表结构时,可以明确指定比较采用的规则:
SELECT s.* FROM sg_corporatesign s JOIN pj_billsignrecord d ON d.signFullName COLLATE utf8mb4_unicode_ci = s.name COLLATE utf8mb4_unicode_ci AND d.signStatus = 2;COLLATE在 MySQL 中表示指定字符串的比较和排序规则,也可以理解为使用显式统一的规则来进行比较。当然,也不会修改字段本身的定义或保存的数据。
(2)、统一相关字段的排序规则
如果两个字段长期用于关联,通常应该在结构层面统一定义。假设已经确定sg_corporatesign表的name字段,与pj_billsignrecord表的signFullName字段的排序规则应该设置为相同。
ALTER TABLE pj_billsignrecord MODIFY COLUMN signFullName VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL;三、小结
总上所述,COLLATE用于指定字符串的比较和排序规则,决定比较时是否区分大小写、重音等。字符集相同的字段,也可能因为排序规则不同,在关联或等值比较时触发Illegal mix of collations错误。临时处理可以在 SQL 中通过COLLATE明确比较规则;长期解决则应根据业务需求,统一相关字段的排序规则,并验证查询结果、唯一约束和索引使用情况。