如何在MySQL的FIRST_VALUE、LAST_VALUE窗口函数中实现IGNORE NULLS?
在MySQL窗口函数中实现IGNORE NULLS效果
MySQL的FIRST_VALUE、LAST_VALUE等窗口函数本身不支持IGNORE NULLS参数,要实现忽略NULL值取第一个/最后一个非空值的效果,可通过以下几种方法实现。
场景说明
现有sales表结构及数据如下:
| SaleID | CustomerID | SaleRegion |
|---|---|---|
| 1 | 7 | CITY A |
| 2 | 10 | NULL |
| 3 | 10 | NULL |
| 4 | 7 | CITY A |
| 5 | 10 | CITY B |
| 6 | 10 | CITY C |
需求:按CustomerID分区,为每一行获取该分区内SaleRegion的第一个非空值,期望输出:
| CustomerID | FIRST_VALUE(SALEREGION) |
|---|---|
| 7 | CITY A |
| 7 | CITY A |
| 10 | CITY B |
| 10 | CITY B |
| 10 | CITY B |
方法1:排序调整结合FIRST_VALUE
通过ORDER BY将非空值行前置,同时扩展窗口范围覆盖整个分区,确保即使当前行前全是NULL,也能取到分区内第一个非空值:
SELECT CustomerID, FIRST_VALUE(SaleRegion) OVER ( PARTITION BY CustomerID ORDER BY CASE WHEN SaleRegion IS NOT NULL THEN SaleID ELSE 999999 END ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS `FIRST_VALUE(SALEREGION)` FROM sales ORDER BY CustomerID, SaleID;
逻辑说明
CASE WHEN SaleRegion IS NOT NULL THEN SaleID ELSE 999999 END:让非空值行按原SaleID排序,NULL值行被排到末尾,保证第一个非空值成为窗口内第一行。ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING:将窗口范围设为整个分区,确保所有行都能获取到分区内的第一个非空值。
方法2:子查询预取非空值再关联
先通过子查询找到每个CustomerID对应的第一个非空SaleRegion,再与原表关联实现值的广播:
WITH first_non_null_region AS ( SELECT CustomerID, SaleRegion FROM sales WHERE SaleRegion IS NOT NULL GROUP BY CustomerID, SaleRegion HAVING MIN(SaleID) = SaleID ) SELECT s.CustomerID, fnnr.SaleRegion AS `FIRST_VALUE(SALEREGION)` FROM sales s JOIN first_non_null_region fnnr ON s.CustomerID = fnnr.CustomerID ORDER BY s.CustomerID, s.SaleID;
逻辑说明
- 子查询通过
MIN(SaleID) = SaleID筛选出每个CustomerID下最早出现的非空SaleRegion。 - 关联原表后,将该非空值映射到对应
CustomerID的所有行上。
方法3:聚合窗口函数(适用于任意非空值场景)
如果不需要严格按顺序取第一个,仅需任意一个非空值,可直接用MAX()/MIN()窗口函数:
SELECT CustomerID, MAX(SaleRegion) OVER (PARTITION BY CustomerID) AS `FIRST_VALUE(SALEREGION)` FROM sales ORDER BY CustomerID, SaleID;
注:此方法会取分区内
SaleRegion的最大值,若需最小值则用MIN(),仅适用于对非空值顺序无要求的场景。
内容的提问来源于stack exchange,提问作者user17276231
相关产品推荐
相关产品推荐

