<p>用AVG()窗口函数计算部门平均工资需写AVG(salary) OVER (PARTITION BY department),可直接用于四则运算,如salary - AVG(salary) OVER (PARTITION BY department),并注意空值处理、排序别名及数据库兼容性。</p>

用 AVG() 窗口函数算部门平均工资
关键不是先 GROUP BY,而是用窗口函数在不打散行的前提下算出每个部门的平均值。直接写 AVG(salary) OVER (PARTITION BY department) 就能为每一行附上其所在部门的平均工资,不需要子查询或 JOIN。
常见错误是写成 AVG(salary) OVER ()(全表平均)或漏掉 PARTITION BY department,结果所有员工都减同一个数,差额就全错了。
- 确保
department字段无空值,否则空值会被单独分组,导致该类员工的“部门平均”只基于空值行计算 - 如果部门名含空格或大小写混杂,确认是否需先标准化(如
TRIM(UPPER(department)))再分区 - 浮点精度问题:PostgreSQL/MySQL 8.0+ 默认返回
numeric或double,若要保留两位小数,可套ROUND(..., 2)
用当前行工资减去窗口计算出的部门均值
窗口函数返回的是标量值,可直接参与四则运算。核心表达式就是:salary - AVG(salary) OVER (PARTITION BY department)。
注意别写成 AVG(salary - AVG(salary) OVER (...)) —— 这是语法错误,窗口函数不能嵌套在聚合函数里。
- 结果为正,说明该员工高于部门平均;为负则低于平均
- 某些数据库(如 older SQLite)不支持窗口函数,执行会报错
near "OVER": syntax error,得换用相关子查询 - 如果
salary字段为NULL,整行差额也会是NULL,必要时用COALESCE(salary, 0)处理
加排序和别名让结果更实用
单纯算出差额还不够直观,通常需要按部门、差额大小排序,并起个明确别名方便下游使用。
示例语句:
SELECT name, department, salary, ROUND(salary - AVG(salary) OVER (PARTITION BY department), 2) AS salary_diff_from_dept_avg FROM employees ORDER BY department, salary_diff_from_dept_avg DESC;
-
ORDER BY department, salary_diff_from_dept_avg DESC能快速看出各部门里谁拉高/拖低了均值 - 别名避免出现未命名列,尤其在 Python/Pandas 读取结果时防止列名变成
?column? - 如果部门数量多,考虑加
LIMIT或配合WHERE department IN (...)缩小范围,避免窗口函数扫描全表影响性能
遇到 NULL 或数据倾斜怎么办
当某个部门只有 1 人,AVG() 仍有效(等于本人工资),差额为 0 —— 这是合理行为,不是 bug。
但若部门字段大量为 NULL,PARTITION BY department 会把所有空值归为一组,算出一个“空部门平均”,容易误导。这时候得提前过滤或归类:
- 加条件
WHERE department IS NOT NULL排除无效部门 - 或改用
PARTITION BY COALESCE(department, 'Unknown')把空值统一归类 - 极少数情况(如某部门薪资方差极大),均值可能失真,此时可考虑用中位数替代,但多数 SQL 方言不原生支持
MEDIAN()窗口函数,得手写逻辑
真正容易被忽略的是:窗口函数的执行顺序在 WHERE 之后、ORDER BY 之前。所以过滤必须写在 WHERE,不能指望在窗口里 FILTER (WHERE ...) —— 只有 PostgreSQL 支持该语法,MySQL 和 SQL Server 不认。

















