first_value是窗口函数,返回窗口帧中首行指定列值;它本身不填充缺失值,但配合order by和rows between unbounded preceding and current row,可实现向前填充——需用case when将非null值前置排序,否则遇null仍返回null。

什么是FIRST_VALUE,它为什么能填缺失值
FIRST_VALUE 是窗口函数,返回当前窗口帧(frame)中第一行的指定列值。它本身不“填充”缺失值,但配合 ORDER BY 和合适的窗口范围(比如 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),可以拿到当前行及之前所有非空记录里的第一个有效值——这正是向前填充(forward fill)的核心逻辑。
关键点在于:它依赖排序和窗口定义,不是无条件取表头;如果排序列有重复或缺失,结果可能出人意料。
用FIRST_VALUE做前向填充的典型写法
核心思路是:把原始值作为窗口输入,用 COALESCE 或条件判断把 NULL 替换为该窗口内第一个非空值。
SELECT
dt,
sales,
FIRST_VALUE(sales) OVER (
ORDER BY dt
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS filled_sales
FROM sales_table;
但这样写有问题:FIRST_VALUE(NULL) 仍返回 NULL。真正可用的是:
SELECT
dt,
sales,
FIRST_VALUE(sales) OVER (
ORDER BY CASE WHEN sales IS NOT NULL THEN dt END NULLS LAST,
dt
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS filled_sales
FROM sales_table;
更稳妥的做法是先构造一个“已知有效值序列”:
- 用
ROW_NUMBER() OVER (PARTITION BY grp ORDER BY dt) 配合 SUM(CASE WHEN sales IS NOT NULL THEN 1 ELSE 0 END) OVER (ORDER BY dt) 生成分组标识 grp
- 再对每个
grp 用 FIRST_VALUE(sales) OVER (PARTITION BY grp ORDER BY dt)
常见错误:ORDER BY 写错导致结果全乱
FIRST_VALUE 的行为完全由 ORDER BY 控制。如果排序字段含重复值且没加次级排序,数据库可能任意选第一行:
- 错误写法:
ORDER BY year_month(当多行同月且 sales 为空时,FIRST_VALUE 可能取到后面才出现的非空值)
- 正确写法:
ORDER BY year_month, id 或 ORDER BY year_month, created_at,确保顺序唯一
- 漏掉
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 会导致默认窗口(RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),在时间重复时把多行捆一起,结果错位
比 FIRST_VALUE 更直接的替代方案
PostgreSQL 14+、SQL Server 2022、DuckDB 支持 IGNORE NULLS 修饰符:
LAG(sales) IGNORE NULLS OVER (ORDER BY dt)
但多数 MySQL(≤8.0)、旧版 PostgreSQL 不支持。此时必须用 FIRST_VALUE + 分组技巧,或改用自连接/相关子查询——后者在大数据量下明显变慢。
真正容易被忽略的是:FIRST_VALUE 填充依赖严格单调的时间/序号列。如果源数据存在时间跳变、回填或乱序插入,必须先 ORDER BY 清洗,否则填充结果会跨过真实业务断点。
COALESCE 或条件判断把 NULL 替换为该窗口内第一个非空值。
SELECT
dt,
sales,
FIRST_VALUE(sales) OVER (
ORDER BY dt
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS filled_sales
FROM sales_table;
但这样写有问题:FIRST_VALUE(NULL) 仍返回 NULL。真正可用的是:
SELECT
dt,
sales,
FIRST_VALUE(sales) OVER (
ORDER BY CASE WHEN sales IS NOT NULL THEN dt END NULLS LAST,
dt
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS filled_sales
FROM sales_table;
更稳妥的做法是先构造一个“已知有效值序列”:
- 用
ROW_NUMBER() OVER (PARTITION BY grp ORDER BY dt)配合SUM(CASE WHEN sales IS NOT NULL THEN 1 ELSE 0 END) OVER (ORDER BY dt)生成分组标识grp - 再对每个
grp用FIRST_VALUE(sales) OVER (PARTITION BY grp ORDER BY dt)
常见错误:ORDER BY 写错导致结果全乱
FIRST_VALUE 的行为完全由 ORDER BY 控制。如果排序字段含重复值且没加次级排序,数据库可能任意选第一行:
- 错误写法:
ORDER BY year_month(当多行同月且 sales 为空时,FIRST_VALUE 可能取到后面才出现的非空值)
- 正确写法:
ORDER BY year_month, id 或 ORDER BY year_month, created_at,确保顺序唯一
- 漏掉
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 会导致默认窗口(RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),在时间重复时把多行捆一起,结果错位
比 FIRST_VALUE 更直接的替代方案
PostgreSQL 14+、SQL Server 2022、DuckDB 支持 IGNORE NULLS 修饰符:
LAG(sales) IGNORE NULLS OVER (ORDER BY dt)
但多数 MySQL(≤8.0)、旧版 PostgreSQL 不支持。此时必须用 FIRST_VALUE + 分组技巧,或改用自连接/相关子查询——后者在大数据量下明显变慢。
真正容易被忽略的是:FIRST_VALUE 填充依赖严格单调的时间/序号列。如果源数据存在时间跳变、回填或乱序插入,必须先 ORDER BY 清洗,否则填充结果会跨过真实业务断点。
ORDER BY year_month(当多行同月且 sales 为空时,FIRST_VALUE 可能取到后面才出现的非空值)ORDER BY year_month, id 或 ORDER BY year_month, created_at,确保顺序唯一ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 会导致默认窗口(RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),在时间重复时把多行捆一起,结果错位IGNORE NULLS 修饰符:
LAG(sales) IGNORE NULLS OVER (ORDER BY dt)但多数 MySQL(≤8.0)、旧版 PostgreSQL 不支持。此时必须用
FIRST_VALUE + 分组技巧,或改用自连接/相关子查询——后者在大数据量下明显变慢。
真正容易被忽略的是:FIRST_VALUE 填充依赖严格单调的时间/序号列。如果源数据存在时间跳变、回填或乱序插入,必须先 ORDER BY 清洗,否则填充结果会跨过真实业务断点。











