题目:
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-01 到 2019-12-31 的记录
从 2019-01-01 到 2019-01-03 所有任务成功,系统状态为 "succeeded"。
从 2019-01-04 到 2019-01-05 所有任务失败,系统状态为 "failed"。
从 2019-01-06 到 2019-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