在业务系统的交易模块中,经常需要查询用户最近一次的交易状态,比如判断用户最新的订单是否支付成功、最新的转账是否到账等。使用ROW_NUMBER窗口函数配合倒序排列是实现这个需求的高效方案,下面详细介绍具体的实现逻辑。

需求场景说明
假设我们有一张交易记录表transaction_record,表结构如下:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | int | 交易记录主键 |
| user_id | int | 用户ID |
| trans_time | datetime | 交易时间 |
| trans_status | varchar(20) | 交易状态,如成功、失败、处理中 |
| trans_amount | decimal(10,2) | 交易金额 |
每个用户会有多条交易记录,我们需要查询每个用户最近一次的交易状态以及对应的交易金额、交易时间等信息。
ROW_NUMBER函数基本用法
ROW_NUMBER是SQL中的窗口函数,作用是为结果集中的每一行分配一个唯一的连续整数,整数的值由ORDER BY子句定义的排序规则决定。语法格式如下:
ROW_NUMBER() OVER ( PARTITION BY 分组字段 ORDER BY 排序字段 [ASC|DESC] ) AS 序号别名
其中PARTITION BY是可选参数,用于指定分组的字段,相同分组内的记录会单独排序;ORDER BY用于指定排序的规则,DESC表示倒序,ASC表示正序,默认是ASC。
获取最近一次交易状态的实现步骤
第一步:为每笔交易按用户分组并倒序排序
我们需要先按user_id分组,每个用户的交易记录单独排序,然后按trans_time倒序排列,这样每个用户最新的交易就会排在第一位,序号为1。
SELECT
id,
user_id,
trans_time,
trans_status,
trans_amount,
-- 按用户分组,按交易时间倒序排序,生成序号
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY trans_time DESC
) AS rn
FROM transaction_record
第二步:筛选序号为1的记录
上一步的查询结果中,rn=1的记录就是每个用户最近一次的交易记录,我们只需要筛选出这部分数据即可。
-- 外层查询筛选序号为1的记录
SELECT
user_id,
trans_time AS latest_trans_time,
trans_status AS latest_trans_status,
trans_amount AS latest_trans_amount
FROM (
-- 子查询:为每笔交易生成排序序号
SELECT
id,
user_id,
trans_time,
trans_status,
trans_amount,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY trans_time DESC
) AS rn
FROM transaction_record
) t
WHERE t.rn = 1
注意事项
- 如果同一个用户存在多笔交易时间完全相同的记录,ROW_NUMBER会随机给这些记录分配不同的序号,此时如果需要保留所有时间最新的记录,可以改用
RANK()或者DENSE_RANK()函数,这两个函数会给相同排序值的记录分配相同的序号。 - 如果交易时间字段存在NULL值,倒序排列时NULL值会排在最前面,需要根据业务需求提前处理NULL值,比如用
COALESCE(trans_time, '1970-01-01 00:00:00')给NULL值设置默认时间。 - 这种方式支持MySQL 8.0+、PostgreSQL、SQL Server、Oracle等主流数据库,兼容性较好,不需要写复杂的自连接查询。
性能优化建议
如果transaction_record表的数据量较大,建议在user_id和trans_time字段上建立联合索引,这样窗口函数排序的时候可以直接走索引,避免全表扫描,提升查询效率。
-- 创建联合索引 CREATE INDEX idx_user_trans_time ON transaction_record(user_id, trans_time DESC);
SQLROW_NUMBER交易状态窗口函数修改时间:2026-07-03 11:21:29