使用sum() over(partition by dept)可在不减少行数前提下为每行添加所属部门工资总计,避免子查询或join;partition by与group by机制不同,不可混用。

用 OVER(PARTITION BY ...) 实现明细行带分组总计
直接加 SUM() OVER(PARTITION BY ...) 就能给每行附上它所属分组的汇总值,不需要子查询或 JOIN。关键在于:窗口函数不改变行数,只在每行“旁加一列”结果。
比如查每个部门员工工资明细,同时显示该部门总工资:
SELECT name, dept, salary, SUM(salary) OVER(PARTITION BY dept) AS dept_total FROM employees;
注意:PARTITION BY dept 表示按 dept 分组计算,但每行仍保留原始 name 和 salary,不是聚合后的一行。
PARTITION BY 和 GROUP BY 别混用
如果同时写了 GROUP BY,又在 SELECT 里用 SUM() OVER(...),可能报错(如 PostgreSQL),或语义混乱(MySQL 8.0+ 允许但逻辑难读)。窗口函数和分组聚合是两套机制:
-
GROUP BY→ 行数变少,必须用聚合函数,不能直接选非分组字段 -
OVER(PARTITION BY ...)→ 行数不变,可自由选原始字段 + 窗口计算值 - 想同时有明细、分组总计、全表总计?用多个窗口函数并列写就行
错误写法:
SELECT name, dept, SUM(salary) OVER(PARTITION BY dept) FROM employees GROUP BY dept;(
name 非分组字段,且 GROUP BY dept 后只剩部门级行)
要跨分组算累计或排名?补上 ORDER BY 子句
OVER(PARTITION BY ... ORDER BY ...) 才能支持累计、移动平均、行号等依赖顺序的操作。单纯求和、计数不需要 ORDER BY,加了反而可能影响性能(尤其大数据量时排序开销明显)。
常见组合:
-
COUNT(*) OVER(PARTITION BY dept)→ 每个部门人数(每行都一样) -
SUM(salary) OVER(PARTITION BY dept ORDER BY hire_date)→ 按入职时间累加的部门工资总额 -
ROW_NUMBER() OVER(PARTITION BY dept ORDER BY salary DESC)→ 部门内薪资排名
没写 ORDER BY 时,SUM、AVG 这类聚合窗口函数默认作用于整个分区(即所有同部门行),行为确定;但 ROW_NUMBER 或 LEAD 必须指定顺序,否则报错。
NULL 值和空分组会让 PARTITION BY 行为变微妙
PARTITION BY 字段为 NULL 时,所有 NULL 会被归为同一组(标准 SQL 行为)。如果业务中 dept 允许为空,那所有无部门员工会共用一个“隐式分组”,它们的 dept_total 就是这群人的工资和——这未必是你想要的。
规避方式:
- 提前用
COALESCE(dept, 'UNKNOWN')替换 NULL,让分组更可控 - 加
WHERE dept IS NOT NULL过滤(如果 NULL 确实代表无效数据) - 检查执行计划:某些数据库对含 NULL 的
PARTITION BY可能无法走索引,导致窗口计算变慢
另外,空分组(即 PARTITION BY 后没匹配到任何行)不会产生结果行,这点和 GROUP BY 一致——但窗口函数本身不生成新行,所以不必担心“多出一行”。











