SQL 聚合函数和窗口函数怎么结合使用?

SQL中的聚合函数用于对一组数据进行计算并返回单个结果,比如求和、计数、求平均值等,通常会配合GROUP BY子句对数据进行分组统计,分组后每组只会返回一条汇总结果。窗口函数则是在保持原有数据行数的基础上,对每一行数据进行计算,计算结果会关联到对应的行上,不会减少返回的行数。两者结合使用可以在同一条查询中同时实现分组汇总和明细行展示的需求,不需要多次查询再关联,大幅提升查询效率。
聚合函数与窗口函数的基础特性对比
先明确两者的核心差异,才能更好地理解结合使用的逻辑:
| 特性 | 聚合函数 | 窗口函数 |
|---|---|---|
| 返回结果行数 | 分组后每组1行 | 与原始数据行数一致 |
| 常用语法 | 配合GROUP BY使用 | 配合OVER(PARTITION BY)使用 |
| 典型用途 | 数据汇总统计 | 分组内排序、分组内排名、分组内累计计算 |
两者结合使用的常见场景
场景一:查询明细数据时同时展示分组汇总值
比如我们需要查询每个员工的工资明细,同时需要知道该员工所在部门的总工资、平均工资,不需要先分组统计部门工资再和员工表关联。这时候可以先把聚合函数作为窗口函数使用,在OVER子句中指定分组逻辑即可。
假设有员工表emp,字段包含emp_id(员工ID)、dept_id(部门ID)、salary(工资),示例查询代码如下:
-- 查询员工明细,同时展示部门总工资、部门平均工资 SELECT emp_id, dept_id, salary, -- 聚合函数SUM作为窗口函数,按部门分组计算总工资 SUM(salary) OVER(PARTITION BY dept_id) AS dept_total_salary, -- 聚合函数AVG作为窗口函数,按部门分组计算平均工资 AVG(salary) OVER(PARTITION BY dept_id) AS dept_avg_salary FROM emp;
场景二:分组聚合后做二次窗口计算
有时候我们需要先对数据做分组聚合,得到每个分组的汇总结果,再对这些汇总结果做窗口计算,比如计算每个部门的总工资在所有部门中的排名。这时候可以先通过子查询或者CTE完成分组聚合,再在外层查询中使用窗口函数。
同样以emp表为例,查询每个部门的总工资和该总工资在所有部门中的排名:
-- 先分组聚合得到部门总工资,再计算排名
WITH dept_salary AS (
-- 第一步:用聚合函数分组统计部门总工资
SELECT
dept_id,
SUM(salary) AS total_salary
FROM emp
GROUP BY dept_id
)
-- 第二步:对部门总工资用窗口函数计算排名
SELECT
dept_id,
total_salary,
RANK() OVER(ORDER BY total_salary DESC) AS salary_rank
FROM dept_salary;
场景三:聚合函数与专用窗口函数配合完成复杂统计
比如我们需要查询每个员工的工资,同时知道部门内工资排名,以及部门总工资、部门内工资累计值。这时候可以同时使用聚合类窗口函数和专用窗口函数比如ROW_NUMBER、SUM的累计写法。
示例代码如下:
SELECT emp_id, dept_id, salary, -- 专用窗口函数,计算部门内工资排名 ROW_NUMBER() OVER(PARTITION BY dept_id ORDER BY salary DESC) AS dept_salary_rank, -- 聚合函数作为窗口函数,计算部门总工资 SUM(salary) OVER(PARTITION BY dept_id) AS dept_total_salary, -- 聚合函数作为窗口函数,计算部门内工资累计值(按工资降序累计) SUM(salary) OVER(PARTITION BY dept_id ORDER BY salary DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS dept_cum_salary FROM emp;
结合使用的注意事项
- 不要在同一个SELECT子句中同时使用普通聚合函数和GROUP BY,又同时使用窗口函数,除非窗口函数的OVER子句中不包含和GROUP BY冲突的逻辑,否则会报语法错误。
- 聚合函数作为窗口函数使用时,不需要配合GROUP BY子句,分组逻辑写在OVER的PARTITION BY中即可。
- 窗口函数的执行顺序在WHERE、GROUP BY、HAVING之后,所以如果需要对聚合后的结果做窗口计算,建议用子查询或者CTE先完成聚合,再进行窗口计算。
- 如果聚合函数作为窗口函数时OVER子句中没有PARTITION BY,那么会对全表数据做聚合计算,每一行都会返回同一个全表汇总值。
总结
SQL聚合函数和窗口函数的结合使用核心在于理解两者的定位:聚合函数负责数据汇总,窗口函数负责在明细行上关联计算规则。结合使用的场景主要分为两类,一类是把聚合函数作为窗口函数直接在OVER子句中指定分组,实现明细带分组汇总的需求;另一类是先用聚合函数分组得到汇总结果,再对汇总结果用窗口函数做二次计算。掌握这种结合用法可以减少不必要的表关联,提升复杂查询的开发效率和执行性能。