MySQL嵌套查询中字符集或校对规则不一致会导致索引失效、全表扫描、临时表、filesort或报错;应统一collation、显式指定COLLATE、确保连接层使用utf8mb4,并通过EXPLAIN排查隐式转换问题。

WHERE 子查询字段字符集不一致导致全表扫描
MySQL 在执行 SELECT * FROM t1 WHERE t1.name IN (SELECT name FROM t2) 时,如果 t1.name 是 utf8mb4_general_ci,而 t2.name 是 utf8mb4_unicode_ci,即使两者都是 utf8mb4,也可能触发隐式转换——MySQL 会把 t2.name 转成 t1.name 的排序规则来比对,导致 t2 上的索引失效。
- 用
SHOW CREATE TABLE t1和SHOW CREATE TABLE t2分别确认字段的COLLATE,别只看CHARSET - 显式统一 collation:在子查询里加
COLLATE强制对齐,比如(SELECT name COLLATE utf8mb4_general_ci FROM t2) - 更稳妥的做法是建表时就统一 collation,避免后期补救;
ALTER TABLE t2 MODIFY name VARCHAR(255) COLLATE utf8mb4_general_ci可批量修正 - 注意:collation 不同 ≠ 字符集不同,但 MySQL 5.7+ 对 collation 不一致的 join / in / = 比较更敏感,尤其涉及索引时
JOIN 中 ON 条件字段字符集不匹配引发临时表和 filesort
当写 SELECT * FROM a JOIN b ON a.code = b.code,若 a.code 是 utf8mb4_bin、b.code 是 utf8mb4_0900_as_cs,MySQL 无法直接走索引合并(index merge),大概率退化为 Block Nested-Loop Join,并生成内部临时表 + filesort。
- 执行
EXPLAIN FORMAT=TRADITIONAL看Extra字段是否出现Using temporary; Using filesort或Using join buffer - 不要依赖
CONVERT()或CAST()临时兜底,它们会让b.code失去索引能力;优先改表结构 - 若只能临时修复,用
ON a.code = b.code COLLATE a.code's_collation显式指定转换方向,避免 MySQL 自行选错基准 - MySQL 8.0 默认 collation 是
utf8mb4_0900_as_cs,而老库常用utf8mb4_general_ci,混用时极易中招
子查询返回结果被强制转成 utf8mb3 导致乱码或截断
某些旧版客户端(如老版本 PHP PDO、Navicat)连接时未声明 charset=utf8mb4,或服务端 character_set_client 设为 utf8(即 utf8mb3),会导致嵌套查询返回的字符串被截断或转义异常,比如 emoji 显示为 ??,或中文变问号。
- 检查连接层实际生效的字符集:
SELECT @@character_set_client, @@character_set_connection, @@character_set_results - 在 SQL 前加
SET NAMES utf8mb4是权宜之计;根本解法是客户端连接串中明确指定charset=utf8mb4 - 特别注意存储过程或函数内嵌的子查询:它们继承调用时的连接字符集,不是定义时的字符集
-
utf8在 MySQL 中永远指utf8mb3,不是 UTF-8 全集;任何含 emoji、新汉字、数学符号的字段都必须用utf8mb4
UNION 子查询中列字符集自动提升失败报错
写 (SELECT name FROM t1) UNION (SELECT nickname FROM t2) 时,若 t1.name 是 latin1_swedish_ci、t2.nickname 是 utf8mb4_0900_as_cs,MySQL 会尝试“提升”到更高兼容的 collation,但若两者无公共超集(比如 latin1 和 utf8mb4 没有可兼容的 collation),就会报错 Illegal mix of collations。
- 错误信息一定是
Illegal mix of collations开头,后面跟着两个 collation 名称和操作类型(如 for equal comparison) - 解决方式不是降级,而是显式 cast:把较弱的列转强,例如
SELECT name COLLATE utf8mb4_0900_as_cs FROM t1 - UNION 所有分支的对应列都会参与 collation 推导,哪怕只有一列不一致,整个语句就失败
- 视图定义里藏了 UNION?那这个视图在被其他查询引用时也会复现该问题,排查要顺藤摸瓜
字符集和校对规则的隐式行为,在嵌套查询里比单表查询更隐蔽。最麻烦的不是报错,而是没报错却慢得离谱——因为索引静默失效,你得从 EXPLAIN 的 key 和 rows 两列里自己发现不对劲。










