SQL 报告系统状态的连续日期

题目:
https://leetcode-cn.com/problems/report-contiguous-dates/

系统 每天 运行一个任务。每个任务都独立于先前的任务。任务的状态可以是失败或是成功。

编写一个 SQL 查询 2019-01-01 到 2019-12-31 期间任务连续同状态 period_state 的起止日期(start_date 和 end_date)。即如果任务失败了,就是失败状态的起止日期,如果任务成功了,就是成功状态的起止日期。

最后结果按照起始日期 start_date 排序

查询结果样例如下所示:

Failed table:
+-------------------+
| fail_date         |
+-------------------+
| 2018-12-28        |
| 2018-12-29        |
| 2019-01-04        |
| 2019-01-05        |
+-------------------+

Succeeded table:
+-------------------+
| success_date      |
+-------------------+
| 2018-12-30        |
| 2018-12-31        |
| 2019-01-01        |
| 2019-01-02        |
| 2019-01-03        |
| 2019-01-06        |
+-------------------+


Result table:
+--------------+--------------+--------------+
| period_state | start_date   | end_date     |
+--------------+--------------+--------------+
| succeeded    | 2019-01-01   | 2019-01-03   |
| failed       | 2019-01-04   | 2019-01-05   |
| succeeded    | 2019-01-06   | 2019-01-06   |
+--------------+--------------+--------------+

结果忽略了 2018 年的记录,因为我们只关心从 2019-01-012019-12-31 的记录
从 2019-01-012019-01-03 所有任务成功,系统状态为 "succeeded"。
从 2019-01-042019-01-05 所有任务失败,系统状态为 "failed"。
从 2019-01-062019-01-06 所有任务成功,系统状态为 "succeeded"

解题思路:
首先,将fail表和success表用union联立起来,分别加上标签flag

select fail_date d, ('failed') flag from failed where fail_date like '2019%'
        union
        select success_date s, ('succeeded') flag from succeeded where success_date like '2019%'
        order by d

接着,将该表与自己联立,但是令t1表的日期等于t2表日期的前一天。这一步的目的是比较今天的flag和昨天的flag是否相同,因此需要把昨天的flag和今天的flag拉成同一行。

select fail_date d, ('failed') flag from failed where fail_date like '2019%'
        union
        select success_date s, ('succeeded') flag from succeeded where success_date like '2019%'
        order by d
        ) t1
    left join
        (
        select fail_date d, ('failed') flag from failed where fail_date like '2019%'
        union
        select success_date s, ('succeeded') flag from succeeded where success_date like '2019%'
        order by d
        ) t2
    on datediff(t1.d,t2.d)=1

接下来,我们设定了一个变量@phase,当前一天的flag和今天的flag相同时,就认定为同一个phase,当前一天的flag和今天的不同时,就认定为下一个flag,也就是如下的代码:

@phase:=if(t1.flag=t2.flag,@phase,@phase+1) ph

如此一来,我们就获得了一个时间,flag,和phase状态表。我们只要对phase进行group by,选取最小时间和最大时间,就能够得到最终结果。

完整代码如下:

select flag period_state,min(d) start_date,max(d) end_date
from
    (
    select t1.d, t1.flag,
    @phase:=if(t1.flag=t2.flag,@phase,@phase+1) ph
    from
        (select @phase:=0) p,
        (
        select fail_date d, ('failed') flag from failed where fail_date like '2019%'
        union
        select success_date s, ('succeeded') flag from succeeded where success_date like '2019%'
        order by d
        ) t1
    left join
        (
        select fail_date d, ('failed') flag from failed where fail_date like '2019%'
        union
        select success_date s, ('succeeded') flag from succeeded where success_date like '2019%'
        order by d
        ) t2
    on datediff(t1.d,t2.d)=1
    ) tt
group by ph

你可能感兴趣的:(SQL)