提取品牌名首字母需用substring或left,但数据库兼容性不同;须过滤空值和空格,统一转大写;group by不补全缺失字母,需values构造序列left join;中文数字特殊字符需额外处理;函数索引或冗余字段可提升性能。

用 SUBSTRING 或 LEFT 提取首字母
直接从品牌名字段取第一个字符是分组前提,SUBSTRING(name, 1, 1) 和 LEFT(name, 1) 都能实现,但行为略有差异:MySQL 和 SQL Server 支持 LEFT,PostgreSQL 只认 SUBSTRING;SQLite 两者都支持。注意空值和空字符串——NULL 会被单独分到一组,而 ''(空串)取首字母仍是 '',可能干扰统计。
实操建议:
- 先用
WHERE name IS NOT NULL AND TRIM(name) != ''过滤脏数据 - 统一转大写再取首字母,避免大小写混算:
UPPER(LEFT(name, 1)) - 若品牌名含前导空格,务必先
TRIM(name)
GROUP BY 首字母 + COUNT(*)
分组统计本身很简单,但容易漏掉“没有品牌”的字母——比如 A–Z 中某些字母根本没出现,GROUP BY 不会自动补 0。如果业务需要完整字母表展示,就得用 VALUES 构造字母序列再 LEFT JOIN,而不是只靠 GROUP BY。
基础写法(只显示有品牌的首字母):
SELECT UPPER(LEFT(TRIM(name), 1)) AS first_letter, COUNT(*) AS cnt FROM brands WHERE name IS NOT NULL AND TRIM(name) != '' GROUP BY UPPER(LEFT(TRIM(name), 1)) ORDER BY first_letter;
关键点:
-
ORDER BY first_letter必须写,否则结果顺序不可控 - 别在
SELECT里用别名first_letter做GROUP BY(部分数据库不支持,如旧版 MySQL) - 聚合函数外的字段必须出现在
GROUP BY中,否则报错
处理中文、数字和特殊字符
品牌名首字符可能是中文(如“小米”)、数字(如“3M”)或符号(如“@Home”),直接按字节取首字符会出错。MySQL 的 LEFT(name, 1) 对 UTF8MB4 中文是安全的,但 PostgreSQL 需用 SUBSTRING(name FROM 1 FOR 1) 才可靠;SQL Server 的 LEFT 默认按字符而非字节,也OK。
若需区分中英文场景:
- 中文品牌通常按拼音首字母归类,得依赖扩展函数(如 MySQL 的
CONVERT(name USING gbk)配合LEFT,或 PostgreSQL 的unaccent()+ 正则) - 数字开头的品牌建议单独归为 “#” 组:
CASE WHEN LEFT(name, 1) ~ '^[0-9]' THEN '#' ELSE UPPER(LEFT(name, 1)) END(PostgreSQL 语法) - 首字符为空格、破折号等,应提前清洗或归入 “Other” 组
性能与索引注意事项
对 LEFT(name, 1) 直接 GROUP BY 无法走普通 B-tree 索引,因为这是函数操作。想加速,得建函数索引(PostgreSQL/MySQL 8.0+ 支持)或冗余字段。
更稳妥的做法:
- 加一个计算列
first_letter AS UPPER(LEFT(TRIM(name), 1)) STORED,再对其建索引 - 或者业务写入时就维护
first_letter字段,避免每次查都计算 - 如果表超百万行且查询频繁,函数索引比全表扫描快一个数量级
真正麻烦的是跨库兼容性——同一个语句在 MySQL、PostgreSQL、SQL Server 上可能要微调函数名和空值处理逻辑,别指望写一次跑 everywhere。











