在业务数据分析中,分层统计是非常常见的需求,比如电商场景中需要统计每个品类、每个子品类以及全平台的商品销量,或者企业销售数据中需要按大区、省份、城市三个层级统计销售额。SQL SELECT语句可以通过多种方式实现这类分层统计需求,下面介绍几种常用的实现方案。
一、使用GROUP BY结合WITH ROLLUP实现分层统计
WITH ROLLUP是MySQL等数据库中GROUP BY的扩展功能,它可以在分组统计的基础上,自动生成更高层级的汇总行,非常适合实现层级递进的统计需求。
假设我们有一张销售数据表sales_data,表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| region | VARCHAR | 大区名称 |
| province | VARCHAR | 省份名称 |
| city | VARCHAR | 城市名称 |
| amount | DECIMAL | 销售额 |
如果需要按大区、省份、城市三个层级统计销售额,同时生成每个大区的总计、全国的总计,可以使用以下SQL:
-- 按大区、省份、城市分层统计销售额,同时生成高层级汇总 SELECT COALESCE(region, '全国总计') AS region, COALESCE(province, '大区总计') AS province, COALESCE(city, '省份总计') AS city, SUM(amount) AS total_amount FROM sales_data GROUP BY region, province, city WITH ROLLUP;
上述语句中,WITH ROLLUP会在分组结果后追加汇总行:首先会生成每个大区下所有省份、城市的统计,然后生成每个大区的汇总行(此时province和city字段为NULL),最后生成全国的汇总行(region、province、city都为NULL)。COALESCE函数用来把NULL值替换成可读性更高的汇总标识。
二、使用窗口函数实现分层统计
如果数据库支持窗口函数(比如MySQL 8.0+、PostgreSQL、SQL Server等),可以通过窗口函数分别统计不同层级的汇总数据,再将结果整合。
同样以销售数据表为例,需要同时展示每个城市的销售额、所在省份的销售额、所在大区的销售额以及全国总销售额,可以使用以下SQL:
SELECT region, province, city, SUM(amount) AS city_amount, -- 统计省份层级总计 SUM(SUM(amount)) OVER (PARTITION BY region, province) AS province_amount, -- 统计大区层级总计 SUM(SUM(amount)) OVER (PARTITION BY region) AS region_amount, -- 统计全国总计 SUM(SUM(amount)) OVER () AS country_amount FROM sales_data GROUP BY region, province, city ORDER BY region, province, city;
这种方式的好处是可以同时展示所有层级的统计数据,不需要额外生成汇总行,适合需要并行展示多层数据的场景。窗口函数不会合并原有分组行,而是在原有分组结果的基础上追加汇总列。
三、使用自连接实现分层统计
如果数据库不支持WITH ROLLUP和窗口函数,也可以通过自连接的方式实现分层统计,分别统计不同层级的数据后通过关联字段整合。
实现步骤如下:
- 第一步:统计最低层级(城市)的销售额
- 第二步:统计省份层级的销售额,和城市数据关联
- 第三步:统计大区层级的销售额,和省份数据关联
- 第四步:统计全国总计,和大区数据关联
对应的SQL示例如下:
-- 城市层级统计 WITH city_stat AS ( SELECT region, province, city, SUM(amount) AS city_amount FROM sales_data GROUP BY region, province, city ), -- 省份层级统计 province_stat AS ( SELECT region, province, SUM(amount) AS province_amount FROM sales_data GROUP BY region, province ), -- 大区层级统计 region_stat AS ( SELECT region, SUM(amount) AS region_amount FROM sales_data GROUP BY region ), -- 全国总计 country_stat AS ( SELECT SUM(amount) AS country_amount FROM sales_data ) -- 关联所有层级统计结果 SELECT cs.region, cs.province, cs.city, cs.city_amount, ps.province_amount, rs.region_amount, ct.country_amount FROM city_stat cs LEFT JOIN province_stat ps ON cs.region = ps.region AND cs.province = ps.province LEFT JOIN region_stat rs ON cs.region = rs.region CROSS JOIN country_stat ct ORDER BY cs.region, cs.province, cs.city;
这种方式兼容性更好,几乎所有支持SQL的数据库都可以使用,但是需要写更多的SQL逻辑,执行效率相对前两种方式会低一些。
四、不同方案的选择建议
在实际业务中可以根据数据库支持和需求场景选择合适的方案:
- 如果只需要生成层级递进的汇总行,且数据库支持WITH ROLLUP,优先选择第一种方案,逻辑简单执行效率高
- 如果需要同时展示所有层级的统计数据,且数据库支持窗口函数,优先选择第二种方案
- 如果数据库版本较老,不支持上述两种特性,再选择第三种自连接的方案
注意:WITH ROLLUP生成的汇总行中,分组字段的值会是NULL,需要使用COALESCE或者IFNULL函数做替换,否则展示的时候会出现空值,影响可读性。
SQLSELECT分层统计GROUP_BYWITH_ROLLUP修改时间:2026-06-15 09:30:48