mysql默认order by对中文排序不准,因其按unicode码点而非拼音排序;解决方法包括convert转gbk编码、应用层预存拼音字段、或mysql 8.0+用生成列,但原生不支持拼音排序。

MySQL默认ORDER BY对中文排序为什么不准
MySQL的ORDER BY直接对中文字段排序时,实际按字符编码(如UTF8MB4对应的Unicode码点)比较,不是按拼音。比如“张”(U+5F20)和“李”(U+674E),Unicode值前者更大,但拼音“zhang”应排在“li”之后——结果完全反了。
根本原因是:MySQL没内置拼音转换逻辑,它只认字节序或collation定义的比较规则。
- 常见错误现象:
SELECT name FROM user ORDER BY name;返回“王、李、张、陈”,而非“陈、李、王、张” - 即使字段用
utf8mb4_unicode_ci或utf8mb4_general_ci,也只是影响大小写和重音,不解决拼音顺序问题 - 用
utf8mb4_zh_0900_as_cs(MySQL 8.0+)也仅支持简体中文二进制排序,仍非拼音
用CONVERT + COLLATE实现近似拼音排序(兼容5.7+)
最轻量、无需额外函数或扩展的方案:把中文转成GBK编码再按GBK排序——因为GBK中汉字基本按拼音首字母分段排列,且同音字大致有序。虽不完美,但对大多数姓名/地名场景足够可靠。
实操写法:
SELECT * FROM user ORDER BY CONVERT(name USING gbk) COLLATE gbk_chinese_ci;
- 必须同时用
CONVERT(... USING gbk)和COLLATE gbk_chinese_ci,缺一不可;只转编码不指定collate,MySQL仍按默认规则比较 - 字段原始编码需为
utf8mb4(主流),否则CONVERT可能失败或乱码 - 性能影响:无法走索引,全表扫描+临时文件排序,大数据量慎用
- 局限:多音字(如“重庆”的“重”读chóng还是zhòng)无法区分;生僻字或扩展A/B区汉字可能位置偏移
MySQL 8.0+ 使用GENERATED COLUMN + ngram全文索引(精准但有代价)
如果必须严格按拼音首字母甚至完整拼音排序,且用的是MySQL 8.0+,可借助生成列预存拼音首字母,再建索引加速。
示例(以姓名为例):
ALTER TABLE user ADD COLUMN py_first CHAR(1) AS (LEFT(ELT(CONV(HEX(LEFT(name,1)),16,10),16,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'),1)) STORED;
上面只是示意逻辑,实际需配合拼音映射表或UDF。更现实的做法是:
- 应用层处理:插入/更新时,用Python/Java调用
pypinyin或hanlp生成py_sort_key字段存入MySQL - 数据库层建普通索引:
ALTER TABLE user ADD INDEX idx_py_sort (py_sort_key); - 查询时:
ORDER BY py_sort_key,完全可控且能走索引 - 不要试图在SQL里实时调用拼音函数——MySQL无原生拼音函数,UDF维护成本高、安全风险大
ORDER BY结合LOCATE查拼音首字母(应急小技巧)
临时查少量数据、又不能改表结构时,可用LOCATE配合拼音首字母字符串粗筛:
SELECT * FROM user ORDER BY LOCATE(LEFT(name,1), '吖八嚓哒妸发噶哈丌击咔垃妈拏噢啪期然仨他哇吓丫匝');
- 这个字符串是按拼音首字母顺序排列的常用汉字首字集合(含“吖”到“匝”),
LOCATE返回位置即隐含排序权重 - 优点:纯SQL,零依赖;缺点:覆盖不全(漏“孙”“吴”等)、无法处理多字词、性能差
- 仅建议用于后台管理页导出前的简单排序,别用在API或高频查询中
真正可靠的拼音排序永远发生在应用层或预计算字段里。MySQL的ORDER BY本身不是为拼音设计的,硬靠collation或CONVERT只是权宜之计——边界 case 和性能损耗比想象中更常出现。











