很多人在 SQL 里对中文字段做排序或分组时,会以为直接 ORDER BY 或 GROUP BY 就能按拼音首字母排好——但现实很骨感,数据库默认是按 Unicode 码点来的,跟拼音一点关系都没有。想按首字母分组,必须显式地把拼音信息提取出来,这一步绕不过去。

那么,有哪些可行的方案?我们一一拆开看。
MySQL 8.0+:用 CONVERT() + COLLATE 提取拼音首字母
MySQL 确实没有内置的拼音函数,但通过字符集转换这个巧办法,可以曲线救国。核心思路是:先把中文字段从 utf8mb4 转成 gbk 或 gb2312 编码,然后利用 COLLATE 触发拼音排序规则(比如 gbk_chinese_ci),再结合 LEFT() 和 CONVERT() 把首字母提取出来。
不过这里有几个硬性前提:
- 字段字符集必须是
gbk或gb2312,utf8mb4不支持拼音 collation。 - 执行前先检查一下服务器上有没有安装对应的 collation:
SHOW COLLATION LIKE 'gbk%';
示例查询如下:
SELECT UPPER(LEFT(CONVERT(name USING gbk) COLLATE gbk_chinese_ci, 1)) AS first_letter, COUNT(*) AS cntFROM users WHERE name REGEXP '^[u4e00-u9fa5]'GROUP BY first_letterORDER BY first_letter;
⚠️ 注意几个坑:如果字段里混了英文字母、数字或者空格,REGEXP 过滤不严的话,首字母可能会变成乱码或者空值。另外,gbk_chinese_ci 在某些 MySQL 版本中可能不可用,需要先确认。
PostgreSQL:用 unaccent + citext 配合自定义映射表
PostgreSQL 也没有原生的拼音函数,但可以借助扩展来打组合拳。思路是:启用 unaccent 扩展去音调,再用 citext 忽略大小写,最后靠一张手动维护的汉字到首字母映射表来做 JOIN 分组。
具体步骤:
- 先启用扩展:
CREATE EXTENSION IF NOT EXISTS unaccent; - 建一张映射表
hz_pinyin_first,包含两列:hz CHAR(1)(汉字)和first_letter CHAR(1)(对应拼音首字母)。 - 关键点在于:直接用
LEFT(unaccent('zh-CN', name), 1)对纯汉字是没效果的,必须走映射表 JOIN。
示例查询:
SELECT f.first_letter, COUNT(*) AS cntFROM users uJOIN hz_pinyin_first f ON SUBSTRING(u.name FROM 1 FOR 1) = f.hzWHERE u.name ~ '^[u4e00-u9fa5]'GROUP BY f.first_letterORDER BY f.first_letter;
⚠️ 这个方案的硬伤是:映射表需要覆盖所有可能的首字,生僻字很容易漏掉。而且 unaccent 对中文没实际作用,不能替代映射关系。用 SUBSTRING 取首字符时要注意 UTF-8 多字节安全性,不过 PostgreSQL 在这块处理得还算靠谱。
通用稳妥方案:应用层预计算拼音首字母并存入冗余字段
说到底,数据库层面做拼音分组,稳定性和性能都不如在写入时就计算好首字母,存到单独字段(比如 name_pinyin_first),再对这个字段建索引。这才是真正可靠的通用方案。
实现起来也很直接:
- Python 可以用
pypinyin库:lazy_pinyin('张三', style=Style.FIRST_LETTER)[0]就能返回'z'。 - Ja va 可用
pinyin4j,记得设置HanyuPinyinOutputFormat的caseType为UPPERCASE。 - 这个字段可以设为
GENERATED ALWAYS AS (...)(MySQL 5.7+/PG 12+ 都支持),或者由应用层、触发器来维护。 - 查询时直接
GROUP BY name_pinyin_first,又快又稳。
⚠️ 但多音字问题依然是绕不开的坎。比如「重庆」的「重」到底是读 chong 还是 zhong?这类情况没法全自动处理,必须依靠业务约定或人工校验。另外,如果字段内容发生变更,冗余值也要同步更新。
讲到最后,真正难的不是“怎么写 SQL”,而是“谁来保证每个汉字都映射对了首字母”。尤其是当数据来源多样、包含方言用字或新造人名时,拼音库版本、多音字策略、甚至输入法导致的异体字,都会让纯 SQL 方案在边界 case 上突然失效。这也是为什么应用层预计算方案始终是更稳妥的选择。
