left(name, 1) 对中文分组失效,因其取utf8下首字节而非拼音首字母;mysql 8.0+需用映射表、预计算或应用层生成pinyin_first字段实现准确分组。

直接用 LEFT(name, 1) 分组,只对纯英文或 ASCII 字符有效;对中文,它返回的是字节首字,不是拼音首字母——结果完全不可信。
为什么 LEFT(name, 1) 对中文分组会失效
中文字符在 UTF8 或 UTF8MB4 下占 3 字节,LEFT(name, 1) 取的是第一个字节(比如 0xE5),根本不是“张”“赵”“周”的拼音首字母。你看到的分组结果往往是乱码、空值,或大量汉字被挤进同一组(比如所有 GBK 编码首字节为 0xD5 的字全归成一组)。
常见错误现象:
-
SELECT LEFT(name, 1), COUNT(*) FROM user GROUP BY LEFT(name, 1)返回几十个或重复的单字(如全是“李”) - 明明有“王”“吴”“魏”,却都分到同一个分组里
- 排序看起来像按拼音,但分组统计数和实际人名首字母对不上
MySQL 8.0+ 中真正能用的拼音首字母提取方式
MySQL 不提供原生拼音函数,CONVERT(name USING gbk) + COLLATE 只影响排序行为,无法用于 GROUP BY 提取首字母。必须靠映射或预计算。
可行路径:
- 建一张拼音首字母映射表(26 条记录),字段含
first_letter和对应 Unicode 码点范围(如 A: U+963F–U+964A),再用UNICODE(LEFT(name,1))关联查询 - 用
ELT(INTERVAL(...))配合 GBK 区位码硬编码(仅适用于一级常用汉字,且 MySQL 版本需稳定支持 HEX/CONV) - 最稳妥:在应用层或 ETL 过程中生成
pinyin_first列并加索引,SQL 层只做GROUP BY pinyin_first
SQL Server 中别再滥用 CONVERT 做拼音分组
CONVERT(VARCHAR, name) 本身不产生拼音,起作用的是后面的 COLLATE。但 COLLATE 只在比较和排序时生效,不能用于 GROUP BY 中提取首字母。
正确做法:
- 用自定义函数(如
dbo.FuncSysGetPY(name))先转拼音,再LEFT(dbo.FuncSysGetPY(name), 1)分组 - 或者改用
UNICODE(LEFT(name COLLATE Chinese_PRC_CS_AS_KS_WS, 1))结合映射表查首字母(注意:这仍不是拼音,只是排序位置参考) - 避免写
CONVERT(VARCHAR, name) COLLATE ...而不指定长度,容易截断导致首字丢失
PostgreSQL 和 Oracle 完全不用碰 CONVERT
PostgreSQL 根本没有 CONVERT(... USING ...) 函数,硬套会报错 function convert does not exist。应改用 unaccent() + upper() + 正则映射,或提前物化拼音列。
Oracle 支持 NLSSORT(name, 'NLS_SORT=SCHINESE_PINYIN_M') 排序,但同样无法直接用于分组提取首字母;必须配合子查询或函数封装(如用 REGEXP_SUBSTR 配合拼音转换表)。
真正复杂的地方不在语法,而在于:拼音首字母不是确定性编码属性,而是语言学映射。一个“重”字可能是 Chóng 或 Zhòng,数据库没上下文就无法判断。所以所有“实时计算”方案,本质都是妥协——要么覆盖不全,要么性能差,要么要维护映射表。生产环境强烈建议把 pinyin_first 当成业务字段来存、来索引、来更新。










