计算分组内占比应使用SUM() OVER(PARTITION BY dept)作分母,而非COUNT(*);趋势分析需用LAG()/LEAD()对比相邻周期占比,而非对占比取滑动平均;须用NULLIF和COALESCE处理除零与NULL,且分组逻辑须严格对齐业务口径。

用 SUM() OVER() 算分组内占比,别直接除以 COUNT(*)
分组内占比(比如“每个部门里女性员工占多少”)本质是:当前行值 / 当前分组总和。常见错误是先 GROUP BY 再 JOIN 回原表,或者误用 COUNT(*) OVER(PARTITION BY ...) 去除以人数——但占比分母该是**指标总和**,不是记录数。
正确做法是用窗口聚合算分母:
-
SUM(salary) OVER(PARTITION BY dept)得到每个部门工资总和 - 再用当前行
salary / SUM(salary) OVER(PARTITION BY dept)得占比 - 注意:如果
salary为NULL,整个表达式结果为NULL,必要时用COALESCE(salary, 0)
示例:
SELECT dept, name, salary,
ROUND(salary * 100.0 / SUM(salary) OVER(PARTITION BY dept), 2) AS pct_in_dept
FROM employees;
趋势要用 LAG() 或 LEAD(),不是再套一层 AVG() OVER()
“占比趋势”指占比值随时间/序号的变化方向(比如“上月占比 vs 本月占比”),不是对占比取滑动平均。直接对占比列用 AVG() OVER(ORDER BY month ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) 是平滑,不是趋势分析。
要看出变化,核心是比对相邻周期的占比值:
- 先算出每期的分组占比(如上一步)
- 再用
LAG(pct_in_dept, 1) OVER(PARTITION BY dept ORDER BY month)拿上期值 - 相减得变动量:
pct_in_dept - LAG(pct_in_dept) OVER(...) - 注意
ORDER BY必须明确,否则LAG结果不可靠;PARTITION BY要和占比计算一致,否则跨组拉数据
示例(按月统计部门薪资占比及环比变动):
WITH dept_month_pct AS (
SELECT dept, month,
SUM(salary) * 100.0 / SUM(SUM(salary)) OVER(PARTITION BY dept ORDER BY month) AS pct
FROM payroll
GROUP BY dept, month
)
SELECT dept, month, pct,
ROUND(pct - LAG(pct) OVER(PARTITION BY dept ORDER BY month), 2) AS delta
FROM dept_month_pct;
ROUND() 和除零必须显式处理,数据库不会自动兜底
窗口函数本身不规避数学异常。当某分组总和为 0(比如某月某部门没发薪),salary / SUM(salary) OVER(...) 会报 division by zero 错误(PostgreSQL、SQL Server)或返回 NULL(MySQL 8.0+ 默认),行为不统一。
- 用
NULLIF(SUM(salary) OVER(...), 0)把分母为 0 变成NULL,让整除结果为NULL而非报错 - 再用
COALESCE(..., 0)统一补 0(按业务需求定) -
ROUND(x, 2)的第二参数是小数位数,不是百分比精度;要显示“23.45%”,得自己拼字符串或乘 100 后再ROUND
安全写法:
ROUND(
COALESCE(
salary * 100.0 / NULLIF(SUM(salary) OVER(PARTITION BY dept), 0),
0
),
2
) AS pct
性能瓶颈常在 PARTITION BY 列未索引或数据倾斜
窗口函数执行时,数据库需对每个分组做排序或哈希聚合。如果 PARTITION BY 字段(如 dept)基数极低(只有 3 个部门)或极高(50 万用户 ID),都可能拖慢:
- 低基数:所有数据挤进少数几个分区,单个分区处理量大,CPU 成瓶颈
- 高基数:分区太多,内存管理开销上升,部分引擎(如 Hive)甚至 OOM
- 解决方案:检查执行计划中
WindowAgg节点的Partitions数和Memory Usage;对高频PARTITION BY字段建索引(仅对支持索引加速窗口的引擎有效,如 PostgreSQL 16+ 的INDEXED WINDOW实验特性)
真实场景中,比优化 SQL 更快的往往是预聚合:把“部门月度薪资总额”存成物化视图或中间表,再 JOIN 计算占比,避免每次跑全量窗口。
真正难的不是写出 LAG 或 SUM() OVER,而是确认“分组逻辑是否和业务口径完全一致”——比如“部门”是指组织架构快照日的部门,还是入职时的部门?这个细节错一点,趋势图就全偏了。










