必须先用 LEFT 或 SUBSTRING 提取前缀再 GROUP BY,不可直接在 GROUP BY 中对原始字段做前缀分组;LEFT 语义更清晰,主流数据库均支持,但 Oracle 用 SUBSTR(name,1,3),PostgreSQL 旧版需 SUBSTRING(name FROM 1 FOR 3);NULL 值需 COALESCE 处理;变长前缀须配合分隔符函数如 SUBSTRING_INDEX 或 SPLIT_PART;函数索引可优化性能,MySQL 8.0+、PostgreSQL 支持表达式索引,SQL Server 需计算列;COLLATION 影响大小写敏感性,必要时用 BINARY 或 LOWER 统一处理。

用 SUBSTRING 或 LEFT 提取前缀再 GROUP BY
直接在 GROUP BY 中对原始字段做前缀分组不可行,必须先截取前缀。主流数据库都支持 SUBSTRING(或别名 SUBSTR)和 LEFT,语义更清晰的是 LEFT —— 它专为取左端字符设计,不易出错。
常见错误是写成 GROUP BY column_name LIKE 'abc%',这语法不合法,LIKE 是条件运算符,不能当分组表达式用。
-
LEFT(name, 3)取前 3 个字符;MySQL、PostgreSQL(v15+)、SQL Server、SQLite 都支持 - PostgreSQL 旧版本或某些严格模式需用
SUBSTRING(name FROM 1 FOR 3) - Oracle 用
SUBSTR(name, 1, 3),注意起始位置从 1 开始(不是 0) - 如果字段可能为
NULL,LEFT(NULL, 3)结果仍是NULL,会被归为同一组,必要时加COALESCE(LEFT(name, 3), 'unknown')
处理变长前缀(如按域名一级域分组)
当“前缀”不是固定长度,而是由分隔符界定(例如邮箱的 @ 后部分、路径的首级目录),就不能硬写 LEFT。得用字符串定位函数配合截取。
例如统计用户邮箱所属一级域名:
SELECT SUBSTRING_INDEX(email, '@', -1) AS domain, COUNT(*) FROM users GROUP BY domain;
MySQL 中 SUBSTRING_INDEX 最方便;PostgreSQL 要用 SPLIT_PART(email, '@', 2);SQL Server 得靠 STRING_SPLIT + 子查询或 CHARINDEX + SUBSTRING 组合。
- 避免用
LIKE+GROUP BY模拟分组(如CASE WHEN email LIKE '%@gmail.com' THEN 'gmail'...),维护成本高且漏匹配 - 若分隔符可能不存在(如脏数据里没
@),SUBSTRING_INDEX返回原字符串,SPLIT_PART报错,需提前WHERE email LIKE '%@%'过滤或用NULLIF处理
性能影响:前缀字段要不要建索引?
在 GROUP BY LEFT(column, N) 场景下,普通 B-Tree 索引对原始字段 column 几乎无效——因为数据库无法用索引直接定位“前 N 位相同”的范围。
- MySQL 8.0+ 支持函数索引:
CREATE INDEX idx_name_prefix ON table_name ((LEFT(name, 3))),括号写法是关键 - PostgreSQL 可建表达式索引:
CREATE INDEX idx_name_prefix ON users (LEFT(name, 3)) - SQL Server 不支持函数索引,只能考虑计算列 + 索引:
ALTER TABLE users ADD name_prefix AS LEFT(name, 3),再对name_prefix建索引 - 如果表很大且该分组查询频繁,不加函数索引很可能触发全表扫描,延迟明显
注意 COLLATION 和大小写敏感性
前缀分组结果是否区分大小写,取决于字段的排序规则(COLLATION)。例如 utf8mb4_0900_as_cs 是大小写敏感,utf8mb4_0900_ai_ci 是不区分大小写且忽略重音。
这意味着 LEFT('User', 2) 和 LEFT('USER', 2) 在不同 collation 下可能被分到同一组或不同组。
- 查当前字段 collation:
SHOW FULL COLUMNS FROM table_name LIKE 'column_name'(MySQL) - 临时强制大小写敏感分组可加
BINARY:GROUP BY BINARY LEFT(name, 3)(MySQL) - PostgreSQL 默认区分大小写,如需不区分,用
LOWER(LEFT(name, 3))并确保索引也基于该表达式
这个细节常被忽略,尤其当业务方反馈“张三”和“张叁”被分到一组,或“API”和“api”统计数对不上时,先看 collation。

















