NTH_VALUE 是什么
NTH_VALUE 是窗口函数,取窗口帧内第 N 行的值,典型用法如「每个用户的第二笔订单时间」「每个品类销量第二的商品」。结果完全取决于帧的范围:不写帧时用的是默认帧(从分区开头到当前行),N 大于当前行号就取不到值、直接返回 NULL。想取整个分区的第 N 行,必须把帧显式写全。
基本信息
| 语法 | NTH_VALUE(表达式, N) OVER (PARTITION BY ... ORDER BY ... 帧定义) |
|---|---|
| 计数起点 | 1 表示帧内第一行,N 超出帧范围时返回 NULL |
| 帧的影响 | 默认帧只到当前行,取分区第 N 行须显式写到 UNBOUNDED FOLLOWING |
| 排序的影响 | 分区内排序不确定时,第 N 行是谁也不确定 |
| 计数方向 | 部分实现提供从末尾开始数的修饰符,跨库使用前先查对应手册 |
典型使用场景
- 第二笔订单时间:衡量复购节奏,取分组内按时间排序的第二行
- 榜单取值:取某维度销量第二的商品,避开最大值异常
- 同窗口多取值:一次窗口同时取第一行与第 N 行做对比
- 与首末值交叉验证:和 FIRST_VALUE、LAST_VALUE 配合核对顺序逻辑
- 固定位次报表:需要「第几个」而不是「第几名」时比排名函数更直接
- 差值检测:比较第 N 行与第一行的差额,观察分布尾部
取分组内第二个值
取分组内第二个值
SELECT user_id, created_at,
NTH_VALUE(created_at, 2) OVER ( # 取该用户第二笔订单的时间
PARTITION BY user_id
ORDER BY created_at
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING # 帧覆盖整个分区
) AS second_order_at
FROM orders;
SELECT DISTINCT user_id, # 每用户保留一行
NTH_VALUE(amount, 2) OVER (PARTITION BY user_id ORDER BY created_at, id
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS second_amount
FROM orders; # 排序键补上 id,保证行序唯一返回值逐种情况读法
| 情况 | 返回值 | 说明 |
|---|---|---|
| N 落在帧范围内 | 该行表达式的值 | 前提是行序已经确定 |
| N 超出帧范围 | NULL | 默认帧下 N 大于当前行号时最常发生 |
| 第 N 行本身为空 | NULL | 与「行不存在」无法区分,需配合计数列分辨 |
| 排序键存在并列 | 取决于实现决定的行序 | 并列行的先后不定,第 N 行可能变 |
| 帧写成 ROWS | 按物理行号逐个计数 | 换成 RANGE 时并列行会按同一批处理 |
「分区的第 N 行」和「到当前行为止的第 N 行」是两回事,差别只在帧定义上,结果却完全不同。业务上说「第二笔」时几乎都指前者,帧一定要写全,别依赖默认值。
只想拿相邻行用 LEAD/LAG,只想拿首末行用 FIRST_VALUE/LAST_VALUE 更直白;如果需求是「按大小排名第 N」,那是名次语义,应该用 RANK、DENSE_RANK 配合过滤,而不是按行位置取值的 NTH_VALUE。
分区与排序的组合决定结果能否复现:排序键有并列值时,行序由实现决定,同一份数据在不同版本或不同执行计划下可能给出不同的第 N 行。关键指标要给排序键补一个能唯一定序的列(例如主键 id)。