跳到主要内容

ROW_NUMBER 窗口函数详解

一、一句话理解

ROW_NUMBER() 给每一行分配一个唯一的序号,从 1 开始。即使两行数据完全相同,序号也绝不重复。

二、函数语法

ROW_NUMBER() OVER (PARTITION BY 分组列 ORDER BY 排序列)
  • PARTITION BY:可选,按指定列分组,每组内独立编号
  • ORDER BY:必选,决定编号顺序

三、真实业务场景

场景1:每组取 Top N

查询每个部门薪资最高的 3 名员工。

SELECT dept, name, salary
FROM (
SELECT dept, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employee
) t
WHERE rn <= 3;

示例数据

+----------+--------+--------+
| dept | name | salary |
+----------+--------+--------+
| 技术部 | 张三 | 50000 |
| 技术部 | 李四 | 45000 |
| 技术部 | 王五 | 42000 |
| 技术部 | 赵六 | 40000 |
| 销售部 | 孙七 | 48000 |
| 销售部 | 周八 | 35000 |
+----------+--------+--------+

结果(每个部门取前3):

+----------+--------+--------+
| dept | name | salary |
+----------+--------+--------+
| 技术部 | 张三 | 50000 |
| 技术部 | 李四 | 45000 |
| 技术部 | 王五 | 42000 |
| 销售部 | 孙七 | 48000 |
| 销售部 | 周八 | 35000 |
+----------+--------+--------+

注意:ROW_NUMBER 即使 salary 相同也不并列。如果赵六和王五薪资相同都是 42000,ROW_NUMBER 会随机给其中一人编号 3,另一人编号 4,导致只取到其中一人。如果需要并列结果,改用 DENSE_RANK()

场景2:去重保留最新记录

用户表有多条记录(每次修改都插入一条),取每个用户的最新一条。

SELECT user_id, name, email, update_time
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY update_time DESC) AS rn
FROM user_history
) t
WHERE rn = 1;

四、与 RANK / DENSE_RANK 的对比

函数相同值处理示例 (A=100, B=100, C=90)
ROW_NUMBER不并列A=1, B=2, C=3
RANK并列跳号A=1, B=1, C=3
DENSE_RANK并列不跳号A=1, B=1, C=2

五、常见坑点

坑1:ORDER BY 不能省略

ROW_NUMBER 必须指定排序,否则结果是随机的。面试中忘记写 ORDER BY 直接扣分。

坑2:相同值顺序不确定

当 ORDER BY 的列有相同值时,ROW_NUMBER 的分配是随机的。需要稳定排序时,应在 ORDER BY 中加第二排序键(如主键)。

六、面试怎么考

  • "ROW_NUMBER 和 RANK 有什么区别?" → 字节/阿里高频
  • "怎么查询每个部门工资前3的员工?" → 子查询 + ROW_NUMBER
  • "怎么去重只保留最新一条记录?" → PARTITION BY + ORDER BY DESC + rn=1

七、相关面试题

📱关注公众号

「数据仓库技术」文章同步更新,不错过每一篇干货

微信公众号二维码
💬加群交流

备注「数据仓库技术」加入社群,每日一道大厂SQL真题

交流微信二维码

你可能还想看