多列分组是什么
GROUP BY 写多列时,按这些列取值的组合来分组,结果行数等于实际出现过的组合数。列的顺序会影响中间排序与索引利用,但不会改变结果集合。多列分组常用来一次产出多维度的交叉统计。
基本信息
| 语义 | 按列的取值组合分组,每个组合一行 |
|---|---|
| 顺序 | GROUP BY a, b 与 GROUP BY b, a 结果集相同,中间过程可能不同 |
| 索引 | 复合索引按最左前缀匹配分组列可省去排序步骤 |
| 结果行数 | 等于实际组合数,比单列分组更容易放大 |
| HAVING | 对分组后的聚合结果过滤,例如只留数量大于 1 的组合 |
| 与窗口函数 | 需要保留明细行时用窗口函数而不是分组 |
两列组合统计
两列组合统计
SELECT city, status,
COUNT(*) AS cnt, -- 每个组合的记录数
SUM(amount) AS amount_sum -- 每个组合的金额合计
FROM users
GROUP BY city, status -- 组合分组
HAVING COUNT(*) > 0 -- 过滤分组结果
ORDER BY city, status; -- 分组后顺序不保证,必须显式排序转成二维交叉表
转成二维交叉表
-- 把某一列的取值变成列,做简单交叉表
SELECT city,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS active_cnt, -- 启用数
SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS inactive_cnt -- 停用数
FROM users
GROUP BY city
ORDER BY city; -- 展示友好的形态,比多列分组更直观要点与风险
| 要点 | 说明 | 风险 |
|---|---|---|
| 分组粒度 | 组合数由数据分布决定 | 组合爆炸会让结果行数远超预期 |
| 列顺序 | 不影响结果集 | 与索引顺序不一致时排序成本上升 |
| NULL 分组 | NULL 自成一组的语义按库而异 | 容易出现一行“空值”稀释统计 |
| HAVING | 对聚合结果过滤 | 与 WHERE 位置写错结果不同 |
| 索引匹配 | 遵循最左前缀 | 前导列缺失时索引帮助有限 |
典型使用场景
- 多维交叉统计:城市加状态、渠道加品类
- 分组去重统计:按组合看有多少个不同主体
- 排查重复数据:同一业务键出现多行时用组合分组定位
- 报表钻取:先按粗粒度分组,再按细粒度组合展开
- 对比不同维度的分布,判断数据是否均衡
- 为下游宽表准备聚合结果
NULL 会自成一个分组:某列没有取值的记录会聚成一行,不看清楚就会稀释统计。需要排除时在 WHERE 里加 IS NOT NULL,需要保留时要在口径说明里点出来。
多列分组的行数容易失控:组合数接近各列基数的乘积,上线前先用 SELECT COUNT(*) 包一层分组子查询估算结果行数,避免把明细表扫爆。