在业务报表中,我们常常需要回答类似“每个地区的销售额占全部销售额多少”或者“某类商品在它所属品类中的销售占比是多少”的问题。这类需求本质上是分组内的占比分布计算,以及分组求和与总计求和之间的比例计算。使用传统的子查询或者自连接也能实现,但SQL中的窗口函数让逻辑更加清晰高效。

基础数据准备
假设我们有一张销售记录表 sales,结构如下:
| region | category | amount |
|---|---|---|
| 华东 | 食品 | 100 |
| 华东 | 饮料 | 50 |
| 华北 | 食品 | 80 |
| 华北 | 饮料 | 70 |
使用窗口函数计算占比
通过 SUM(amount) OVER() 可以得到所有记录的总计,通过 SUM(amount) OVER(PARTITION BY region) 可以得到每个地区的总计。两者相除就是该地区占整体的比例。
SELECT region, category, amount, SUM(amount) OVER (PARTITION BY region) AS region_total, SUM(amount) OVER () AS grand_total, amount * 1.0 / SUM(amount) OVER (PARTITION BY region) AS rate_in_region, SUM(amount) OVER (PARTITION BY region) * 1.0 / SUM(amount) OVER () AS region_rate FROM sales;
代码说明
- region_total 表示按地区分组后的销售额合计
- grand_total 表示全表销售额总计
- rate_in_region 是具体商品在该地区内的占比
- region_rate 是该地区销售额占全部销售额的比例
仅统计分组求和与总计比例
如果只需要每个分组的总计及其占总体比例,可以先聚合再使用窗口函数:
SELECT region, SUM(amount) AS region_total, SUM(SUM(amount)) OVER () AS grand_total, SUM(amount) * 1.0 / SUM(SUM(amount)) OVER () AS region_rate FROM sales GROUP BY region;
注意事项
在计算比例时,建议将分子乘以 1.0 或者使用 CAST 转换为小数类型,避免整数除法导致结果恒为 0。另外,窗口函数中的 PARTITION BY 决定了分组边界,需要根据业务口径正确设置。
占比分析是报表开发中的常见场景,掌握窗口函数可以显著降低SQL复杂度。
小结
使用 SQL 的窗口函数能够在一次扫描中同时获得分组求和与总计求和,从而轻松计算分组内的占比分布以及分组与总计之间的比例。相比子查询和自连接,这种方式可读性更好,也更利于数据库优化执行计划。