在业务系统的数据查询需求中,按特定字段分组并获取每组最新记录是非常常见的场景,比如获取每个用户最近的一次登录记录、每个订单最新的状态变更信息等。使用窗口函数ROW_NUMBER可以简洁高效地实现这类需求,无需编写复杂的嵌套子查询。

ROW_NUMBER窗口函数基本语法
ROW_NUMBER是SQL标准中的窗口函数之一,它的作用是为分区内的每一行记录分配一个唯一的连续整数,序号从1开始。基本语法结构如下:
-- ROW_NUMBER基本语法
ROW_NUMBER() OVER (
PARTITION BY 分组字段
ORDER BY 排序字段 [ASC|DESC]
) AS 序号别名
其中PARTITION BY用于指定分组的字段,相当于GROUP BY的作用,但是不会合并行;ORDER BY用于指定每个分组内记录的排序规则,通常我们会按时间字段降序排列,这样最新的记录就会排在第一位,对应的序号就是1。
实际场景示例:提取每个用户的最新订单
假设我们有一张订单表order_info,表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| order_id | INT | 订单ID,主键 |
| user_id | INT | 用户ID |
| order_amount | DECIMAL | 订单金额 |
| create_time | DATETIME | 订单创建时间 |
现在需要查询每个用户的最新一笔订单信息,使用ROW_NUMBER的实现步骤如下:
第一步:为分组记录生成排序序号
先按user_id分组,每个用户组内的记录按create_time降序排列,生成序号:
SELECT
order_id,
user_id,
order_amount,
create_time,
-- 按用户分组,组内按创建时间降序排序,生成序号
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY create_time DESC
) AS rn
FROM order_info;
第二步:筛选序号为1的记录
上一步查询的结果中,每个用户的最新订单对应的rn值为1,我们只需要筛选rn = 1的记录即可得到目标结果:
-- 最终查询语句,获取每个用户的最新订单
SELECT
order_id,
user_id,
order_amount,
create_time
FROM (
SELECT
order_id,
user_id,
order_amount,
create_time,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY create_time DESC
) AS rn
FROM order_info
) tmp
WHERE rn = 1;
与传统方法的对比
在窗口函数普及之前,开发者通常会用子查询的方式实现分组取最新记录,示例如下:
-- 传统子查询方式实现分组取最新记录
SELECT
o1.order_id,
o1.user_id,
o1.order_amount,
o1.create_time
FROM order_info o1
WHERE o1.create_time = (
SELECT MAX(o2.create_time)
FROM order_info o2
WHERE o2.user_id = o1.user_id
);
对比两种实现方式,ROW_NUMBER的优势非常明显:
- 逻辑更清晰:不需要多层嵌套关联,代码可读性更高
- 性能更好:对于大表数据,窗口函数的执行效率通常高于自关联子查询
- 功能更灵活:如果需要取每组的前N条记录,只需要修改WHERE条件中的序号值即可,而传统方式需要大幅调整逻辑
注意事项
使用ROW_NUMBER提取分组最新记录时需要注意两个问题:
- 如果分组内存在多条记录的排序字段值完全相同,ROW_NUMBER会随机给这些记录分配不同的序号,此时如果需要保留所有相同最新时间的记录,可以改用
RANK()或者DENSE_RANK()窗口函数 - 排序字段的选择要符合业务需求,通常选择创建时间、更新时间这类单调递增的时间字段,避免使用可能重复的字段作为排序依据
窗口函数ROW_NUMBER是SQL中处理分组排序场景的利器,熟练掌握它的用法可以大幅提升分组查询的编写效率和执行性能,建议在日常开发中可以优先使用这种方式实现分组取最新记录的需求。
SQLROW_NUMBER窗口函数分组查询最新记录修改时间:2026-07-17 19:30:25