订阅更新

系统状态的连续日期段:日期减行号找区间

SQL 实战 123 题 · 第 82 题 困难 约 2 分钟读完

LeetCode 1225 · 连续区间(gaps and islands)、UNION ALL、行号差

本文目录
  1. 面试怎么回答
  2. 题意
  3. 表结构
  4. 思路
  5. 参考解法
  6. 易错点

面试怎么回答

经典的 gaps and islands。把失败、成功两张表 UNION ALL 成(日期, 状态),过滤到 2019 年;再按状态分区用 DATE_SUB(日期, INTERVAL ROW_NUMBER() 天) 得到区间编号,连续日期的编号相同;最后按状态和编号分组取 MIN、MAX 日期。连续登录、连续打卡都是这个套路。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

题意

失败表记录任务失败的日期,成功表记录任务成功的日期,系统每天恰好运行一次。只看 2019 年,把连续相同状态的日期合并成区间,输出状态、开始日期和结束日期,按开始日期排序。

表结构

  • Failed:fail_date(主键)
  • Succeeded:success_date(主键)

思路

先把两张表合成一张「日期 + 状态」的流水(UNION ALL,并只保留 2019 年)。然后按状态分区、日期排序编号,用「日期减去行号」:同一状态下连续的日期,减去连续的行号后得到同一个值,这个值就是区间编号。最后按(状态, 区间编号)分组,取最小和最大日期。

参考解法

select type  period_state, min(date) start_date, max(date) as end_date
from
(
    select type, date, subdate(date,row_number()over(partition by type order by date)) as diff
    from
    (
        select 'failed' as type, fail_date as date from Failed
        where fail_date between '2019-01-01' and '2019-12-31'
        union all
        select 'succeeded' as type, success_date as date from Succeeded
        where success_date between '2019-01-01' and '2019-12-31'
    ) a
)b
group by type,diff
order by start_date

易错点

  • 过滤 2019 年的条件要在编号之前做,否则 2018 年年底的数据会和 2019 年初连成一个区间。
  • 按状态分区编号是关键:不分区的话,不同状态交替时差值不再稳定。
刷题进度保存在当前浏览器