LAG函数不能填充缺失值,仅能取前一行字段值;需先用generate_series补全时间轴,再LEFT JOIN原始数据,最后用LAG(... IGNORE NULLS)或COALESCE实现向前填充。

LAG 函数本身不能填充缺失值,只能访问前一行
LAG() 是窗口函数,作用是“取上一行的某个字段值”,它不生成新行、不补数据、也不理解时间间隔是否连续。如果你的时间序列里缺了某天(比如 2024-01-03 没记录),LAG() 在 2024-01-04 这行返回的仍是 2024-01-02 的值——中间那个空档依然存在。
真正要“填充缺失时间点”,得先用 generate_series() 把完整时间轴补出来,再左连接原始数据,最后才轮到 LAG()(或 COALESCE() + 窗口累计)做向前填充。
用 generate_series() 构建完整时间轴再 LEFT JOIN
这是最可控的方式:明确指定起止时间和步长,确保每个时间点都有一行,哪怕原始表没对应记录。
- 假设原始表叫
sensor_readings,含字段recorded_at(timestamptz)和value - 想补全 2024-01-01 到 2024-01-10 每天 00:00 的记录,用
generate_series('2024-01-01'::date, '2024-01-10'::date, '1 day') -
LEFT JOIN时注意匹配精度:如果原始时间含时分秒,而你要按日对齐,得用date(recorded_at)或recorded_at::date
SELECT
gs.dt AS recorded_at,
COALESCE(sr.value, LAG(sr.value) IGNORE NULLS OVER (ORDER BY gs.dt)) AS filled_value
FROM generate_series('2024-01-01'::date, '2024-01-10'::date, '1 day') AS gs(dt)
LEFT JOIN sensor_readings sr ON gs.dt = sr.recorded_at::date
ORDER BY gs.dt;
LAG(... IGNORE NULLS) 是 PostgreSQL 11+ 才支持的关键能力
旧版本(IGNORE NULLS,遇到空值会中断前向传播。此时必须改用递归 CTE 或窗口累计逻辑模拟,复杂度陡增。
- PostgreSQL ≥ 11:直接写
LAG(value) IGNORE NULLS OVER (ORDER BY ts)即可跨过 NULL 向前找最近非空值 - PostgreSQL ROW_NUMBER() OVER (PARTITION BY grp ORDER BY ts) 分组打标,其中
grp是用条件累计和构造的“有效值分段编号” - 若只做简单后向填充(用上一个非空值覆盖后续所有空),
MAX(value) FILTER (WHERE value IS NOT NULL) OVER (ORDER BY ts ROWS UNBOUNDED PRECEDING)更稳定
性能和边界情况必须检查
时间跨度大时,generate_series() 会产生大量中间行;而 LAG(...) IGNORE NULLS 在无索引排序字段上会强制排序,可能拖慢查询。
- 确保
ORDER BY字段(如recorded_at)上有索引,尤其是和generate_series()结合使用时 - 如果原始数据存在重复时间戳,
LEFT JOIN可能导致行数膨胀,应提前去重或加DISTINCT ON - 首行缺失时,
LAG()返回 NULL,且IGNORE NULLS也无前值可取——这个 NULL 得靠外部逻辑处理(比如用默认值COALESCE(..., 0)) - 跨天/跨月边界要注意时区:用
timestamptz+AT TIME ZONE显式转换,避免generate_series()生成的时间和业务时区错位
真正麻烦的不是写法,而是确认“缺失”到底是真数据丢失,还是采集周期本就不固定——后者强行插值反而引入误导性平滑。










