根本原因是字符集与排序规则在客户端、连接、数据库、字段、字面量等多层不一致;需统一character_set_client/connection/results为utf8mb4,建库建表指定utf8mb4_unicode_ci,创建存储过程前执行SET NAMES utf8mb4,并对中文字面量加_utf8mb4前缀或显式COLLATE。

存储过程里中文变问号、等号报错、ORDER BY 排序错乱,根本不是代码写错了,而是字符集和排序规则在多个层级悄悄打架。
为什么存储过程一执行就报 Illegal mix of collations?
错误本质是两个字符串参与比较(比如 =、IN、JOIN)时,各自的排序规则不兼容。MySQL 不会自动转码,直接拒绝执行。
- 常见组合:
utf8mb4_0900_bin和utf8mb4_bin不能直接比;utf8mb4_unicode_ci和gb18030_bin更不行 - 变量的排序规则来自所在数据库的
collation_database,不是你 DECLARE 时写的类型 - 表字段的排序规则是建表时定死的,哪怕库是 utf8mb4,列仍可能是 latin1
- 字符串字面量(如
'张三')默认用连接的collation_connection解析,极易被带偏
怎么让存储过程里的中文不变成问号?
关键不是改过程体,而是确保「创建时」和「执行时」两阶段都用对了编码上下文。
- 建过程前必须先执行:
SET NAMES utf8mb4;—— 否则CREATE PROCEDURE语句里的中文注释、字面量就被当 latin1 存进系统表了 - 检查当前会话三值:
SHOW VARIABLES LIKE 'character\_set%';,确认character_set_client、character_set_connection、character_set_results全是utf8mb4 - 硬编码中文一律加字符集引导符:
_utf8mb4'用户不存在',而不是'用户不存在'—— 这能绕过会话级设置,强制按 utf8mb4 解析 - 动态 SQL 拼接时,别直接
CONCAT(@name),要用CONVERT(@name USING utf8mb4)或_utf8mb4@name(后者仅限 MySQL 8.0+)
如何避免 ORDER BY 或 GROUP BY 中文排序错乱?
中文排序不是字符集问题,是排序规则没选对。utf8mb4_general_ci 或 utf8mb4_0900_as_cs 都不按拼音排,结果就是「重庆」排在「北京」前面。
- 查字段真实排序规则:
SELECT column_name, collation_name FROM information_schema.COLUMNS WHERE table_schema = 'your_db' AND table_name = 'your_table'; - 建表时显式指定:
name VARCHAR(50) COLLATE utf8mb4_pinyin_ci(需 MySQL 8.0.30+ + pinyin 插件)或退而求其次用utf8mb4_unicode_ci - 查询时临时修正:
ORDER BY name COLLATE utf8mb4_unicode_ci,但别依赖它——长期方案是改字段级 collation - 注意:
ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4不会改排序规则,必须明确写COLLATE utf8mb4_unicode_ci
最易被忽略的一点:ALTER DATABASE ... COLLATE 只影响新对象,默认不修旧字段;而存储过程一旦创建完成,其内部变量的排序规则就固化在定义里了——改库、改表都没用,只能重建过程并确保 SET NAMES utf8mb4 在第一步执行。

















