窗口函数识别连续段落的核心是行序差值法:用全局行号减去按达标状态分组的局部行号,相同差值即为同一连续段;需先过滤达标记录或打标,再计算每段长度并取最大值。

用窗口函数识别连续段落
连续达标问题本质是“把相邻的达标记录聚成一组”,关键在构造分组标识。直接 GROUP BY 日期或ID没用,必须借助行序差值法:用全局行号减去按条件筛选后的局部行号,相同差值即属同一连续段。
常见错误是只用 ROW_NUMBER() 而不区分达标/未达标状态——必须先过滤或打标。例如,假设表 t_daily 有 date 和 is_pass(1=达标),先加标记列:
SELECT *,
ROW_NUMBER() OVER (ORDER BY date) -
ROW_NUMBER() OVER (PARTITION BY is_pass ORDER BY date) AS grp
FROM t_daily
WHERE is_pass = 1;
这个 grp 就是连续段 ID。注意:PARTITION BY is_pass 是为了确保只对达标行做局部编号,否则差值会错乱。
计算每段连续天数并取最大值
有了 grp,接下来就是常规分组聚合:
- 每段长度 =
COUNT(*) - 段起止时间 =
MIN(date)/MAX(date) - 全局最长连续天数 =
MAX(COUNT(*))(需嵌套或用窗口)
实际常用写法是两层查询:
SELECT MAX(cnt) AS max_consecutive_days
FROM (
SELECT COUNT(*) AS cnt
FROM (
SELECT *,
ROW_NUMBER() OVER (ORDER BY date) -
ROW_NUMBER() OVER (PARTITION BY is_pass ORDER BY date) AS grp
FROM t_daily
WHERE is_pass = 1
) t
GROUP BY grp
) t2;
如果要同时返回哪一段最长,就把 MIN(date) 和 MAX(date) 也带上。别漏掉外层 WHERE is_pass = 1,否则未达标行会干扰 ROW_NUMBER() 的全局序号。
处理日期不连续但逻辑连续的场景
真实数据常有周末、节假日缺失,比如只记录工作日,2024-01-01 和 2024-01-03 中间缺了 2024-01-02(周日),但业务上仍算连续两天。
这时不能依赖物理日期差,得转为“按日历顺序排列后相邻”。做法是:
- 先生成完整日历序列(或用
GENERATE_SERIES等补全) - 左连接原始表,填充
is_pass - 再跑上面的行号差值逻辑
PostgreSQL 可用:
SELECT d.date, COALESCE(t.is_pass, 0) AS is_pass
FROM GENERATE_SERIES('2024-01-01'::date, '2024-12-31'::date, '1 day') AS d(date)
LEFT JOIN t_daily t ON d.date = t.date;
MySQL 用户得自己建日历表或用递归 CTE。重点在于:连续性判断必须基于完整时间轴,而非原始数据中的日期间隙。
性能与边界情况提醒
- 大表慎用多次
ROW_NUMBER():两个窗口函数会各自排序,IO 和内存开销翻倍。加 date 字段索引能缓解,但超千万行建议预计算 grp 到物化列。
- 边界易错点:
-
is_pass 为 NULL 时,PARTITION BY is_pass 会让 NULL 单独成组,应显式写成 PARTITION BY COALESCE(is_pass, 0) 或先过滤
- 起始/结束日期跨年、跨月不影响算法,但若需按自然月统计连续段,得额外加
YEAR(date) 或 DATE_TRUNC('month', date) 条件
- 如果“达标”定义是动态的(如每日指标 > 阈值),记得把计算逻辑放在子查询里,避免在窗口函数中嵌套复杂表达式
ROW_NUMBER():两个窗口函数会各自排序,IO 和内存开销翻倍。加 date 字段索引能缓解,但超千万行建议预计算 grp 到物化列。-
is_pass为 NULL 时,PARTITION BY is_pass会让 NULL 单独成组,应显式写成PARTITION BY COALESCE(is_pass, 0)或先过滤 - 起始/结束日期跨年、跨月不影响算法,但若需按自然月统计连续段,得额外加
YEAR(date)或DATE_TRUNC('month', date)条件 - 如果“达标”定义是动态的(如每日指标 > 阈值),记得把计算逻辑放在子查询里,避免在窗口函数中嵌套复杂表达式
连续段识别看着绕,核心就一句:用两个不同粒度的序号相减。差值稳定,段落就稳。其余都是围绕它补条件、压性能、防空值。











