SQL Server中计算分组内占比最稳妥的方法是SUM() OVER(PARTITION BY group_col),因其与当前行分组严格对齐、避免整数截断、无需额外过滤且兼容性好。

直接用 SUM() OVER(PARTITION BY group_col) 算分母,再除当前行值,是 SQL Server 里最稳、最可读、也最不容易出错的分组内占比写法。
为什么不能直接在 GROUP BY 后除以子查询总数
常见错误是先 GROUP BY 汇总,再用子查询算总数,比如:
SELECT category, SUM(amount) AS grp_sum,
SUM(amount) / (SELECT SUM(amount) FROM sales) AS pct
FROM sales GROUP BY category;
这看似合理,但实际有三个硬伤:
- 子查询多扫一次表,数据量大时 I/O 和 CPU 开销明显上升
- 如果主查询带
WHERE status = 'completed',子查询却没加,分母就变成全表总数,结果失真 - SQL Server 对整数列做除法会截断小数(如
5 / 10得0),必须显式转浮点
SUM() OVER(PARTITION BY ...) 的正确写法与避坑点
核心是:分母用 SUM(amount) OVER(PARTITION BY category),它和当前行所属分组完全对齐,无需额外过滤或校验。
推荐写法(含防错处理):
SELECT category, name, amount,
ROUND(
100.0 * amount / NULLIF(SUM(amount) OVER(PARTITION BY category), 0),
2
) AS pct_in_category
FROM sales;
关键点:
-
100.0 *强制提升为浮点运算,避免整数除法截断 -
NULLIF(..., 0)必加,否则某组SUM(amount)为 0 时直接报Divide by zero error -
ROUND(..., 2)放在外层,控制最终展示精度;放在中间可能因截断放大误差 - 若
amount可能为NULL,先用COALESCE(amount, 0),否则整组占比可能变NULL
按多字段分组时 PARTITION BY 要写全
比如要算「每个部门每种职级」的薪资占比,PARTITION BY 必须包含所有分组维度:
SELECT dept, title, salary,
ROUND(100.0 * salary / NULLIF(SUM(salary) OVER(PARTITION BY dept, title), 0), 2) AS pct
FROM employees;
漏掉任何一个字段(比如只写 PARTITION BY dept),结果就不是你想要的「部门+职级」组合占比,而是部门内所有职级混在一起算——这种错很难肉眼发现,但报表一上线就露馅。
别把 PERCENT_RANK() 当成占比计算工具
PERCENT_RANK() 返回的是排序位置比例(公式为 (rank-1)/(n-1)),不是「当前值占本组总和多少」。它不看数值大小,只看排序序号。
例如同一部门五人薪资全是 10000,PERCENT_RANK() OVER(PARTITION BY dept ORDER BY salary) 全是 0.0,因为所有人排第 1 名;但他们的薪资占比各是 20%。
真正要算“占本组总和百分比”,只能靠 SUM() OVER(PARTITION BY ...) ——这是语义明确、行为确定、跨数据库兼容的正解。










