last_value默认不跳过null,仅按窗口排序取最后一个值(含null);标准sql中多数数据库(如postgresql、sql server)不支持ignore nulls语法,需用coalesce+lag、子查询或array_agg filter等方法模拟。

LAST_VALUE 真的会跳过 NULL 吗?
不会。LAST_VALUE 默认不忽略 NULL,它只是按窗口排序后取“最后一个值”,哪怕那个值是 NULL。很多人误以为加了 IGNORE NULLS 就能像 Oracle 或 BigQuery 那样自动跳过空值,但标准 SQL(包括 PostgreSQL、SQL Server)的 LAST_VALUE **根本不支持 IGNORE NULLS 语法**——写了会直接报错:ERROR: syntax error at or near "IGNORE"。
只有少数引擎原生支持,比如:
- BigQuery:支持
LAST_VALUE(col IGNORE NULLS) - Oracle:支持
LAST_VALUE(col) IGNORE NULLS - PostgreSQL 16+:仅在
LAG/LEAD中支持IGNORE NULLS,LAST_VALUE仍不支持
PostgreSQL / SQL Server 怎么模拟 LAST_VALUE IGNORE NULLS?
核心思路:用 MAX() + FILTER(PostgreSQL)或子查询 + TOP 1(SQL Server)找窗口内「最近一个非 NULL 值」。不能依赖 LAST_VALUE 直接实现。
PostgreSQL 示例(滑动窗口:当前行及前 2 行):
SELECT
t.*,
(SELECT value FROM t AS t2
WHERE t2.ts t.ts - INTERVAL '2' SECOND
AND t2.value IS NOT NULL
ORDER BY t2.ts DESC LIMIT 1) AS last_nonnull_value
FROM your_table t;
更高效写法(避免关联子查询):
使用数组聚合 + 反向遍历:
SELECT *,
(ARRAY_AGG(value) FILTER (WHERE value IS NOT NULL)
OVER (ORDER BY ts ROWS BETWEEN 2 PRECEDING AND CURRENT ROW))[CARDINALITY(ARRAY_AGG(value) FILTER (WHERE value IS NOT NULL) OVER (ORDER BY ts ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)):]::text[])[1] AS last_nonnull
FROM your_table;
实际中推荐用 LAG 多层回退(最多回溯 N 步):
COALESCE(value, LAG(value, 1) OVER (...), LAG(value, 2) OVER (...))- 适用于固定步数滑动窗口(如前 3 行),清晰且可读性强
为什么不能只用 ORDER BY + ROWS UNBOUNDED PRECEDING?
因为 LAST_VALUE(x) OVER (ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 的「最后」是当前行,不是窗口末尾——它等价于 value 本身。真正要的是「从窗口起点到当前行中,最靠右的非 NULL 值」,这必须显式控制窗口范围并过滤 NULL。
常见错误写法:
-- ❌ 错!这个 LAST_VALUE 永远返回当前行的 value(或 NULL) LAST_VALUE(value) OVER (ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) <p>-- ✅ 对!指定窗口为前 N 行,并用子查询/COALESCE 找非 NULL LAST_VALUE(value) OVER (ORDER BY ts ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) -- 但依然不跳 NULL</p>
关键点:窗口帧定义(ROWS)只决定数据范围,不改变 NULL 处理逻辑;跳过 NULL 是聚合函数或表达式层面的事,不是窗口定义能解决的。
性能和边界情况要注意什么?
滑动窗口填充空值本质是「局部扫描」,数据量大时容易慢。几个硬坑:
- 时间戳列没索引 → 子查询或
LAG链式调用全表扫描 - 窗口跨度大(如前 100 行)→
COALESCE(LAG(...,1), LAG(...,2), ..., LAG(...,100))写起来累,执行计划也可能退化 - 并发更新导致同一窗口内时间戳重复 →
ORDER BY ts不稳定,需加唯一排序键(如ORDER BY ts, id) - PostgreSQL 中
ARRAY_AGG ... FILTER在大窗口下内存开销高,可能触发 work_mem 溢出
如果业务允许近似,考虑先用 LAG 回退 3–5 步,再 fallback 到上一分区的最新非 NULL 值(用 MAX() OVER (PARTITION BY ...)),比暴力扫窗口更可控。










