在SQL数据统计场景中,经常需要计算某一分组内单个值的占比,比如统计每个部门员工薪资占部门总薪资的比例,或者统计各商品销量占所属品类总销量的百分比。使用SUM聚合函数结合OVER子句可以非常高效地实现这类需求,不需要额外编写复杂的子查询或者自连接逻辑。

基础语法说明
OVER子句用于定义窗口函数的窗口范围,结合SUM聚合函数时,可以实现在指定分组内计算总和的效果。基础语法结构如下:
-- 计算分组内总和的语法 SUM(计算字段) OVER (PARTITION BY 分组字段) AS 分组总和
其中PARTITION BY用于指定分组的依据,作用和GROUP BY类似,但不会压缩行数,会为每一行返回对应分组的总和结果。如果要进一步限制窗口范围,还可以添加ORDER BY和ROWS/RANGE子句,不过统计分组总占比时通常不需要额外指定。
分组占比计算完整示例
假设我们有如下的销售额统计表sales_data,结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| category | varchar | 商品品类 |
| product_name | varchar | 商品名称 |
| sale_amount | int | 销售额 |
现在需要统计每个商品销售额占所属品类总销售额的比例,具体实现SQL如下:
SELECT
category,
product_name,
sale_amount,
-- 计算所属品类的总销售额
SUM(sale_amount) OVER (PARTITION BY category) AS category_total,
-- 计算单个商品占品类总销售额的比例,保留4位小数
ROUND(sale_amount * 1.0 / SUM(sale_amount) OVER (PARTITION BY category), 4) AS sale_ratio
FROM sales_data;
上述查询中,第一个SUM结合OVER子句计算了每个品类的总销售额,第二个部分用单个商品销售额除以品类总销售额得到占比,乘以1.0是为了避免整数除法丢失小数部分,ROUND函数用于控制小数位数。
注意事项
- 如果计算字段存在NULL值,SUM聚合函数会自动忽略NULL,不会将其计入总和,如果需要将NULL当作0处理,可以先用
COALESCE(字段, 0)处理后再计算。 - OVER子句中的PARTITION BY可以指定多个分组字段,比如同时按年份和品类分组,只需要写成
PARTITION BY year, category即可。 - 这种计算方式支持所有主流的关系型数据库,包括MySQL 8.0+、PostgreSQL、SQL Server、Oracle等,不同数据库的窗口函数支持版本略有差异,使用前需要确认数据库版本。
扩展场景:累计占比计算
如果需要计算分组内的累计占比,还可以在OVER子句中添加ORDER BY,比如统计每个品类下商品按销售额排序的累计占比:
SELECT
category,
product_name,
sale_amount,
-- 按销售额降序计算累计销售额
SUM(sale_amount) OVER (PARTITION BY category ORDER BY sale_amount DESC) AS cumulative_amount,
-- 计算累计占比
ROUND(SUM(sale_amount) OVER (PARTITION BY category ORDER BY sale_amount DESC) * 1.0
/ SUM(sale_amount) OVER (PARTITION BY category), 4) AS cumulative_ratio
FROM sales_data;
这种写法会在每个分组内按指定顺序累加销售额,从而得到累计占比的结果,适合需要分析头部商品贡献度的场景。