用ROW_NUMBER()配合PARTITION BY和ORDER BY时间字段DESC,再外层筛选rn=1即可;必须加二级排序(如id DESC)防时间重复导致结果不稳定,且窗口函数需嵌套子查询或CTE才能过滤。

怎样用 ROW_NUMBER() 获取每个分类的最新一条记录
直接结论:用 ROW_NUMBER() 配合 PARTITION BY 和 ORDER BY 时间字段 DESC,再外层筛选 rn = 1 即可。关键不是函数本身,而是排序依据是否真正反映“最新”——比如用 created_at 还是 id,得看业务定义。
为什么不能只用 GROUP BY + MAX(time)
常见误区是写 SELECT category, MAX(created_at) FROM t GROUP BY category,这只能拿到时间,拿不到整行数据(比如对应这条记录的 title 或 status)。想查完整记录,必须关联原表或改用窗口函数。用 ROW_NUMBER() 是最直白、兼容性好(MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持)的解法。
实际写法和容易踩的坑
典型结构如下:
SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY category
ORDER BY created_at DESC, id DESC
) AS rn
FROM products
) t WHERE rn = 1;
注意这些细节:
-
PARTITION BY后面填分类字段,比如category、user_id,别漏掉括号 -
ORDER BY必须包含能唯一确定“最新”的字段;如果created_at可能重复,一定要加二级排序(如id DESC),否则ROW_NUMBER()的结果不稳定 - MySQL 5.7 或更早不支持窗口函数,强行运行会报错
This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'类似提示,得换思路(比如用自连接或变量) - 如果表很大,确保
category和created_at上有联合索引,否则PARTITION BY + ORDER BY会全表扫描
替代方案对比:RANK() 和 DENSE_RANK() 为什么不行
这三个函数都做排序编号,但行为不同:ROW_NUMBER() 严格按顺序给唯一序号;RANK() 和 DENSE_RANK() 遇到并列会跳号或不跳号。比如两条记录时间相同,RANK() 会返回 1,1,3 —— 这时 WHERE rn = 1 会取到两条,不符合“每组只取最新一条”的需求。只有 ROW_NUMBER() 能保证结果确定且唯一。
真正麻烦的是时间字段为空、时区不一致、或“最新”其实取决于状态变更日志这类非主键时间字段——这时候光靠 ORDER BY 不够,得先清洗或补全时间值。










