用ROW_NUMBER()配合子查询或CTE取每组最新记录最常用可靠:按user_id分组、order_time倒序编号,取rn=1;需处理NULL(如加NULLS LAST)和并列(改用RANK);老版本可用关联子查询但性能差;禁用GROUP BY混选非聚合字段。

用窗口函数 ROW_NUMBER() 配合子查询或 CTE 是最常用、最可靠的方法。
用 ROW_NUMBER() 按组排序取 Top 1
核心思路:给每组数据按时间(或 ID)倒序编号,取编号为 1 的记录。
假设表 orders 包含字段 user_id、order_time、amount,要查每个用户最新的一笔订单:
SELECT user_id, order_time, amount
FROM (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY order_time DESC
) AS rn
FROM orders
) t
WHERE rn = 1;
注意 NULL 和并列情况
如果 order_time 可能为 NULL,需显式处理,否则这些行可能被排在最前或最后(取决于数据库默认行为):
- 加 NULLS LAST(PostgreSQL、Oracle、SQL Server 2022+ 支持)确保 NULL 排末尾
- MySQL 8.0+ 也支持 NULLS LAST;旧版可用 IFNULL(order_time, '1970-01-01') 临时兜底
- 若同一用户有多条相同最新时间的订单,ROW_NUMBER() 会任意选一条;要全取则改用 RANK() 或 DENSE_RANK(),再把 rn = 1 改成 rn = 1 且保留所有并列项
替代方案:关联子查询(兼容老版本)
不支持窗口函数的老数据库(如 MySQL 5.7)可用相关子查询:
SELECT o1.user_id, o1.order_time, o1.amount FROM orders o1 WHERE o1.order_time = ( SELECT MAX(o2.order_time) FROM orders o2 WHERE o2.user_id = o1.user_id );
⚠️ 注意:这个写法在有重复最大时间时会返回多条,且性能通常不如窗口函数,尤其数据量大时。
避免常见错误
- 别用 GROUP BY user_id + 非聚合字段(如 SELECT user_id, order_time, amount FROM orders GROUP BY user_id),结果不可靠,MySQL 5.7+ 默认拒绝,其他库行为未定义
- 别依赖 ORDER BY ... LIMIT 1 在子查询里直接套,无法按组生效
- 时间字段类型要一致(比如别混用 DATETIME 和 TIMESTAMP),否则排序可能出偏差










