MySQL中文ORDER BY排序错乱主因是COLLATE选错,utf8mb4_unicode_ci等默认规则按Unicode码点而非拼音排序,导致「重庆」排在「北京」前;真正支持拼音排序需MySQL 8.0.30+配合pinyin插件使用utf8mb4_pinyin_ci,否则推荐utf8mb4_unicode_ci兜底,并须显式修改字段级COLLATE而非仅改库级设置。

MySQL 中文 ORDER BY 排序错乱,不是编码问题而是 COLLATE 选错了
中文按拼音排序失败(比如「重庆」排在「北京」前面),大概率不是字符集没设对,而是排序规则(COLLATE)不支持语义排序。MySQL 默认的 utf8mb4_unicode_ci 或 utf8mb4_0900_as_cs 都不按汉字拼音顺序排,它们是基于 Unicode 码点或通用权重规则,对中文不友好。
真正能按拼音排序的,目前只有 MySQL 8.0.30+ 配合 pinyin 插件提供的 utf8mb4_pinyin_ci;若无法升级或装插件,utf8mb4_unicode_ci 是最接近可用的兜底选项——它至少能保证同音字分组相对稳定。
- 查字段真实排序规则:
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(需插件)或utf8mb4_unicode_ci - 查询时临时修正(仅限调试):
ORDER BY name COLLATE utf8mb4_unicode_ci,但别长期依赖——执行计划可能无法走索引 -
ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci会重写所有字段的COLLATE,但大表慎用,锁表且重建索引
SQL Server 中文排序乱码或顺序反常,关键在列级 COLLATE 而非数据库级
改完数据库排序规则为 Chinese_PRC_CI_AS,但视图或查询里中文仍乱码、ORDER BY 结果不符合预期?那是因为已有列的排序规则没变——ALTER DATABASE ... COLLATE 只影响新创建的对象,旧表字段保持原样。
必须逐列确认并修正:SELECT name, collation_name FROM sys.columns WHERE object_id = OBJECT_ID('your_table'); 如果返回的是 SQL_Latin1_General_CP1_CI_AS,哪怕数据库是中文规则,这列依然按拉丁字母逻辑比较和排序。
- 修改单列排序规则:
ALTER TABLE your_table ALTER COLUMN name NVARCHAR(50) COLLATE Chinese_PRC_CI_AS; - 务必用
NVARCHAR替代VARCHAR,否则即使COLLATE正确,存储层也会截断或转码失败 - 跨库 JOIN 时,如果两表字段
COLLATE不同,会直接报错Cannot resolve collation conflict,必须显式加COLLATE Chinese_PRC_CI_AS强制统一 - 不要依赖
SET LANGUAGE N'Chinese'来修复排序——它只影响日期/数字格式,不影响字符串比较逻辑
Oracle 中文排序异常,NLS_SORT 和 NLS_COMP 必须配对启用
Oracle 默认按二进制码点排序,ORDER BY name 出现「张」在「王」前、「李」在「刘」后,不是 bug,是默认行为。要按拼音或笔画排,必须启用语言感知排序(linguistic sort),靠的是 NLS_SORT + NLS_COMP 组合。
单独设 NLS_SORT = SCHINESE_PINYIN_M 不生效,因为 Oracle 还要检查 NLS_COMP 是否允许非二进制比较。默认 NLS_COMP = BINARY,会强制忽略 NLS_SORT 设置。
- 会话级临时启用拼音排序:
ALTER SESSION SET NLS_SORT = 'SCHINESE_PINYIN_M'; ALTER SESSION SET NLS_COMP = 'LINGUISTIC'; - 建索引时如需加速拼音排序,得建函数索引:
CREATE INDEX idx_name_pinyin ON your_table (NLSSORT(name, 'NLS_SORT=SCHINESE_PINYIN_M')); - 应用连接 JDBC 时,不能只靠 URL 参数,必须在连接后执行上述
ALTER SESSION,否则PreparedStatement仍走二进制路径 -
SCHINESE_STROKE_M按笔画数排序,SCHINESE_RADICAL_M按部首排序,注意后缀_M表示多音字兼容模式,比不带_M的更稳妥
动态 SQL 或存储过程中中文参与排序,最容易漏掉字符集引导符
存储过程里写 ORDER BY name 没问题,但一旦拼接变量或字面量,比如 SET @sql = CONCAT('SELECT * FROM t ORDER BY ', @col_name);,就极易因变量本身带错 COLLATE 或隐式转换失败,导致排序异常甚至报错 Illegal mix of collations。
根本原因:变量的排序规则继承自其来源(如来自某字段,则取该字段的 COLLATE;来自字符串字面量,则取当前会话的 collation_connection)。两者不一致时,MySQL 拒绝比较。
- 硬编码中文必须加引导符:
_utf8mb4'北京' COLLATE utf8mb4_unicode_ci,不能只写'北京' - 拼接变量前先转码:
CONVERT(@col_name USING utf8mb4)(兼容所有 MySQL 版本),或_utf8mb4@col_name(MySQL 8.0+) - 动态 SQL 执行前,显式设置会话排序规则:
SET collation_connection = 'utf8mb4_unicode_ci';,避免被上一个查询残留值干扰 - 不要在
ORDER BY后直接跟用户输入字段名,除非已白名单校验且强制加反引号:ORDER BY `@col_name`,否则 SQL 注入风险叠加乱码风险
字符集可以设错再改,但排序规则一旦用于索引或主键约束,调整成本极高;生产环境上线前,必须验证真实中文数据在 ORDER BY 下的输出顺序,不能只看测试用例里的“张三李四”。

















