最稳写法是用sum() over(partition by dept)/sum() over()算部门工资占比,需注意分母对齐、null处理、类型转换和数据库兼容性。

用 SUM() OVER() 算部门工资总和再除,是最稳的写法
直接在 SELECT 里用窗口函数算占比,核心就两步:先算出每个部门的工资总和(SUM(salary) OVER (PARTITION BY dept)),再除以全公司总和(SUM(salary) OVER ())。别用 AVG() 或嵌套子查询,容易错位或性能差。
- 必须用
SUM() OVER(),不是SUM()—— 后者会聚合掉行,没法保留每条员工记录做占比计算 - 分母不能写成常量或单独查一次,否则在有
WHERE条件时结果失真(比如只查技术部员工,分母却还是全公司总和) - 注意
NULL工资值:默认被SUM()忽略,但如果你的业务要求把NULL当 0 处理,得先COALESCE(salary, 0)
百分比结果要四舍五入且带%符号?小心类型转换陷阱
数据库里算出来的通常是小数(如 0.234567),直接 ::TEXT 或拼 '%' 容易出现精度爆炸或报错。PostgreSQL 和 SQL Server 对类型强制更敏感,MySQL 相对宽松但也不可靠。
- 先用
ROUND(..., 4)控制小数位(比如保留 4 位是0.2346),再乘 100 得百分比数值 - 字符串拼接前,务必显式转成
TEXT或VARCHAR,例如:ROUND(100.0 * dept_sum / total_sum, 2)::TEXT || '%' - 如果某部门没数据(
dept_sum为 0),除法会得0.0或NULL,视数据库而定;需要显示'0.00%'就得加COALESCE
GROUP BY 和窗口函数混用时,字段必须全部出现在 GROUP BY 或聚合里
有人想先按部门 GROUP BY 汇总,再算占比,结果报错 column "salary" must appear in the GROUP BY clause。这是典型误解:窗口函数不是聚合函数的替代品,它不改变行数,也不能和未聚合的非分组字段共存于同一层 SELECT。
- 要展示「每个员工占本部门工资比例」,就别
GROUP BY dept,直接用窗口函数 + 原始明细表 - 要展示「每个部门占全公司工资比例」,就该用一层聚合:
SELECT dept, SUM(salary) AS dept_sum FROM emp GROUP BY dept,再套窗口函数或子查询算占比 - 千万别在同一个
SELECT里既写GROUP BY dept,又写SUM(salary) OVER (PARTITION BY dept)—— 大部分数据库会直接拒绝
Oracle 和旧版 MySQL 不支持窗口函数?得换写法
Oracle 11g 及更早、MySQL 8.0 之前都不支持 OVER()。这时候只能用关联子查询或自连接,性能差、写法啰嗦,还容易漏 NULL 处理。
- MySQL 5.7 及以前:用
(SELECT SUM(salary) FROM emp e2 WHERE e2.dept = e1.dept)替代SUM() OVER (PARTITION BY dept) - Oracle 11g:类似,但注意子查询不能相关太深,否则执行计划崩坏;建议建物化视图预计算各部门总和
- 所有兼容写法都要手动处理除零:分母为 0 时返回
0或NULL,否则整条 SQL 报division by zero
实际跑的时候,最常卡在分母没对齐——比如加了 WHERE hire_date > '2020-01-01',但分母还是全量 SUM(salary) OVER (),导致占比加起来远超 100%。这点特别容易被忽略。










