直接用sum()除以总和会出错,因sum(sum())在同一层聚合中非法,窗口函数必须与普通聚合分离;分母须用sum() over(),不可嵌套;整数除法易得0,需转浮点;null值需用coalesce和nullif处理。

为什么直接用 SUM() 除以总和会出错
常见错误是写成 ROUND(SUM(amount) / SUM(SUM(amount)) OVER(), 4) 这类嵌套,SQL 会报错:Cannot perform an aggregate function on an expression containing an aggregate or a subquery。因为 SUM(SUM(...)) 在同一层聚合中非法——窗口函数必须和普通聚合分离处理。
核心原则:先算分子(分组聚合),再用窗口函数算分母(全表或分区总和),两者不能混在同一级 SUM() 内。
- 分母必须用
SUM() OVER(),不是SUM(SUM()) - 如果按类别求占比,
OVER(PARTITION BY category)是错的——那算的是每个类别的内部占比,不是占全局的比例 - 未加
CAST或CONVERT容易因整数除法得 0(比如5 / 100→0)
SUM() OVER() 计算全局占比的正确写法
假设有一张销售表 sales,字段为 region 和 amount,想看各地区销售额占总销售额的百分比:
SELECT
region,
SUM(amount) AS region_total,
ROUND(
CAST(SUM(amount) AS DECIMAL(18,4)) /
SUM(SUM(amount)) OVER(), 4
) AS ratio
FROM sales
GROUP BY region;
注意三点:
-
SUM(amount)是分组聚合,得到每个region的和 -
SUM(SUM(amount)) OVER()是窗口聚合,对上一步的分组结果再求和(即全量总和),语法合法 -
CAST(... AS DECIMAL(18,4))防止整数截断;ROUND(..., 4)控制小数位数
想显示成带 % 符号的字符串怎么办
数据库原生不提供百分比格式化函数,需手动拼接。但要注意:直接 CONCAT(ratio, '%') 可能因精度丢失导致显示异常(如 0.23999999999999999)。
- 先用
ROUND(ratio, 4)截断,再乘 100 转成百分比数值 - 用
CONCAT(ROUND(ratio * 100, 2), '%')更直观 - PostgreSQL 用户可用
TO_CHAR(ratio * 100, 'FM990.00%'),但 MySQL/SQL Server 不支持该语法 - SQL Server 中推荐
FORMAT(ratio * 100, 'N2') + '%'(仅 2012+,且性能略低)
遇到 NULL 或空分组时占比怎么算
如果某 region 的 amount 全为 NULL,SUM(amount) 返回 NULL,整个比值变成 NULL / total → NULL,而非 0。
- 用
COALESCE(SUM(amount), 0)把分子补 0 - 分母
SUM(SUM(amount)) OVER()若全为 NULL,结果也是 NULL,需额外判断:NULLIF(SUM(SUM(amount)) OVER(), 0)配合COALESCE - 更稳妥写法:
COALESCE(CAST(COALESCE(SUM(amount), 0) AS DECIMAL(18,4)) / NULLIF(SUM(SUM(amount)) OVER(), 0), 0)
这种边界情况在报表导出时容易被忽略,一查才发现某些区域“消失”了——其实不是没数据,是占比算出来是 NULL 被前端过滤了。











