中文排序错乱本质是COLLATE不匹配,而非字符集问题;不同校对规则决定字符串比较逻辑,如utf8mb4_general_ci按字节序导致“张三”排在“阿明”后,utf8mb4_0900_as_cs按Unicode码位排序,“重”排“北”前,utf8mb4_unicode_ci则按拼音归类。

ORDER BY 中文排序错乱本质是 COLLATE 不匹配
不是字符集(CHARACTER SET)本身导致顺序异常,而是绑定在字段或连接上的校对规则(COLLATE)决定了「两个中文字符串谁大谁小」。MySQL 比较「北京」和「重庆」时,不看 Unicode 码点,而看当前 COLLATE 定义的权重顺序——utf8mb4_0900_as_cs 把「重」排在「北」前面,utf8mb4_unicode_ci 则按拼音归类,结果完全不同。
常见 COLLATE 对中文排序的影响差异
不同 COLLATE 的设计目标不同,直接决定 ORDER BY 行为:
-
utf8mb4_general_ci:已废弃,不支持拼音/笔画,按字节序粗略比较,「张三」可能排在「阿明」之后 -
utf8mb4_0900_as_cs:区分大小写、重音,但对中文仅按 Unicode 码位排,「龜」和「龟」被当不同字,且「中」 -
utf8mb4_unicode_ci:支持基本 Unicode 归一化,中文按部首/笔画倾向分组,排序更符合直觉 -
utf8mb4_pinyin_ci(MySQL 8.0.30+):需安装 pinyin 插件,真正按拼音首字母排序,「北京」
为什么 SHOW CREATE TABLE 看不出问题?
字段定义里没显式写 COLLATE,它就继承自表默认;表默认又可能继承自数据库默认;而数据库默认可能仍是旧版的 utf8mb4_general_ci。执行 SELECT column_name, collation_name FROM information_schema.COLUMNS WHERE table_name = 'your_table'; 才能看到真实生效的校对规则。更隐蔽的是:@@collation_connection 会覆盖字段级 COLLATE——哪怕字段是 utf8mb4_unicode_ci,连接层用的是 utf8mb4_0900_ai_ci,ORDER BY 仍按后者算。
临时修复 vs 长期根治的取舍
临时加 COLLATE 能救急,但掩盖了结构隐患:
- 临时写法:
ORDER BY name COLLATE utf8mb4_unicode_ci—— 每次查询都得写,索引无法命中,性能差 - 字段级修正:
ALTER TABLE t MODIFY name VARCHAR(50) COLLATE utf8mb4_unicode_ci;—— 生产环境需锁表,但一劳永逸 - 建表规范:
name VARCHAR(50) COLLATE utf8mb4_unicode_ci NOT NULL—— 新表必须显式声明,不能依赖默认值 - 连接初始化:
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;—— 应用启动时统一会话上下文,避免字面量解析偏移
最易被忽略的是:ORDER BY 字段若参与了函数计算(如 UPPER(name) 或 CONCAT('前缀', name)),函数输出默认继承 collation_connection,和原字段 COLLATE 可能冲突——这时连 COLLATE 强制都不管用,得先确保输入源和连接层完全对齐。










