跳转到主内容
极星编程网:以代码为星,赴技术山海!

SQL如何获取每个分组的最后一个值_LAST_VALUE函数应用技巧

LAST_VALUE默认返回当前行值是因为窗口帧为ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW;取分组最后一行完整记录应使用ROW_NUMBER()配合WHERE rn=1,而非LAST_VALUE。 LAST_VALUE 不能直接“取分组最后一个值”,默认行为只返回当前行——因为窗口帧没设对,不是函数本身不行。 LAST_VALUE 为什么总返回当前行的值 直接写
LAST_VALUE(col) OVER (PARTITION BY group_col ORDER BY time_col)
,结果每行都等于
col
自身。这不是 bug,是默认窗口帧为
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
导致的:它只看“从分区开头到当前行”,当前行自然就是“最后”。 必须显式加
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
才覆盖整组 ORDER BY 方向决定“最后”语义:升序时最大时间才是最后;降序时最小时间反而是最后 如果
time_col
有重复(比如并发插入),不加二级排序(如
id DESC
)会导致结果不确定
ignore_nulls
参数仅控制 NULL 是否参与“最后”的判定,不解决帧定义问题 想取整行数据?别硬套 LAST_VALUE
LAST_VALUE
是单列函数,强行用多个
LAST_VALUE
分别取
id
、
name
、
status
,一旦排序键不唯一,各字段可能来自不同记录,造成数据错位。 真正要“获取每组最新一条完整记录”,必须用
ROW_NUMBER()
配合过滤 正确写法:
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY updated_at DESC, id DESC)
,再外层
WHERE rn = 1
用
FIRST_VALUE
+ 降序排序也能绕过帧陷阱,但依然只能取单列,不能替代整行筛选 某些旧版 MySQL(8.0 前)根本不支持窗口函数,
LAST_VALUE
直接报错 比 LAST_VALUE 更稳的替代写法 在 PostgreSQL、SQL Server、MySQL 8.0+、Oracle 中,以下写法更直观、易调试、索引友好:
SELECT user_id, order_id, status, updated_at FROM ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY updated_at DESC, id DESC ) AS rn FROM order_log ) t WHERE rn = 1;
复合索引
(user_id, updated_at, id)
能极大加速该查询 时间相同时靠
id DESC
保证结果稳定,避免非确定性 WHERE 条件必须在外层或 CTE 中写,不能塞进窗口子句——窗口计算发生在 WHERE 之前 别用
RANK()
或
DENSE_RANK()
,它们会在并列时都标为 1,导致多行返回 真正容易被忽略的点是:窗口帧不是可选项,而是默认就错;而“取整行”这个需求,从一开始就不在
LAST_VALUE
的能力范围内。

相关文章