在SQL的实际业务查询中,我们经常会遇到先对数据做分组聚合计算,再对聚合后的结果集进行排名的需求,比如统计每个部门的销售总额后,给部门按销售额从高到低排名。传统实现方式需要先通过子查询得到聚合结果,再在外层查询中做排序赋值,逻辑复杂且容易出错,而窗口函数可以非常优雅地解决这个问题。

一、窗口函数基础概念
窗口函数是SQL中一类特殊的函数,它会对查询结果集的一个子集(也就是窗口)进行计算,且不会改变结果集的行数。和普通的聚合函数不同,窗口函数不需要用GROUP BY做分组,而是可以在保留所有原始行的基础上完成计算。
常用的排名类窗口函数有三个,它们的语法结构一致,只是排名规则有区别:
ROW_NUMBER():按指定顺序给每一行分配唯一的连续序号,相同值的行也会获得不同的序号RANK():按指定顺序排名,相同值的行获得相同序号,下一个序号会跳过重复的数量DENSE_RANK():按指定顺序排名,相同值的行获得相同序号,下一个序号连续不跳过
二、分组聚合后使用窗口函数的语法
如果需要对分组聚合后的结果做排名,正确的做法是先完成分组聚合,再将聚合结果作为窗口函数的输入,结合PARTITION BY和ORDER BY子句实现分组内的排名。
核心语法结构如下:
-- 先分组聚合,再用窗口函数排名
SELECT
分组字段,
聚合结果字段,
排名函数() OVER (PARTITION BY 分组字段 ORDER BY 聚合结果字段 DESC/ASC) AS 排名列
FROM (
-- 子查询:先完成分组聚合
SELECT
分组字段,
SUM(数值字段) AS 聚合结果字段 -- 这里可以是SUM、COUNT、AVG等聚合函数
FROM 数据表
GROUP BY 分组字段
) AS 聚合结果表
其中PARTITION BY用来指定分组的维度,也就是按照哪个字段划分不同的排名组,ORDER BY用来指定排名的排序规则,决定序号的分配顺序。
三、实际场景示例
假设我们有一张销售记录表sales_record,表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| department | VARCHAR | 部门名称 |
| employee_id | INT | 员工ID |
| sale_amount | DECIMAL | 销售金额 |
| sale_date | DATE | 销售日期 |
现在的需求是:统计2024年每个部门的总销售额,然后给所有部门按总销售额从高到低做排名,总销售额相同的部门排名相同,且后续排名不跳过序号。
对应的SQL实现代码如下:
-- 统计部门总销售额并排名
SELECT
department,
total_sale,
-- 使用DENSE_RANK按总销售额降序排名
DENSE_RANK() OVER (ORDER BY total_sale DESC) AS dept_sale_rank
FROM (
-- 子查询:分组聚合计算每个部门的总销售额
SELECT
department,
SUM(sale_amount) AS total_sale
FROM sales_record
WHERE sale_date >= '2024-01-01' AND sale_date <= '2024-12-31'
GROUP BY department
) AS dept_sale_summary
ORDER BY total_sale DESC
如果我们需要的是每个部门内部,给部门员工的销售额做排名,那么只需要在窗口函数中加上PARTITION BY department即可,示例代码如下:
-- 每个部门内给员工销售额排名
SELECT
department,
employee_id,
total_employee_sale,
-- 按部门分区,部门内按员工销售额降序排名
ROW_NUMBER() OVER (PARTITION BY department ORDER BY total_employee_sale DESC) AS emp_sale_rank
FROM (
-- 子查询:聚合每个员工的销售额
SELECT
department,
employee_id,
SUM(sale_amount) AS total_employee_sale
FROM sales_record
GROUP BY department, employee_id
) AS emp_sale_summary
ORDER BY department, total_employee_sale DESC
四、注意事项
1. 窗口函数的执行顺序在WHERE、GROUP BY、HAVING之后,所以在窗口函数中可以直接使用聚合后的字段,不需要额外处理。
2. 如果排名的排序字段有NULL值,需要注意数据库的NULL值排序规则,大部分数据库默认NULL值排在最前面,如果需要调整可以加NULLS LAST或者NULLS FIRST子句,比如ORDER BY total_sale DESC NULLS LAST。
3. 不同的数据库对窗口函数的支持版本不同,MySQL需要8.0及以上版本,SQL Server需要2012及以上版本,Oracle和PostgreSQL较新版本都支持,使用前需要确认数据库版本。
窗口函数大大简化了分组后排名的SQL逻辑,避免了多层子查询的嵌套,不仅代码更易读,执行效率也往往优于传统的自连接排名方式,是SQL查询中非常实用的功能。
SQL窗口函数分组聚合排名ROW_NUMBER修改时间:2026-07-15 03:21:26