• SQL面试题之区间合并问题


    目录

    0 需求

    1 数据准备

    2 数据分析

     2 小结


    0 需求

    给定多个时间段,每个时间段分为开始时间、结束时间,将相互重叠的多个时间段合并为一个区间

    1. --数据:id、开始时间、结束时间
    2. 1 12 15
    3. 2 57 58
    4. 3 29 32
    5. 4 30 31
    6. 5 17 19
    7. 6 44 44
    8. 7 56 57
    9. 8 16 18

    合并后结果如下;

    1. --结果
    2. flag start_time end_time
    3. 1 12 15
    4. 2 16 19
    5. 3 29 32
    6. 4 44 44
    7. 5 56 58

    1 数据准备

    1. create table test02 as
    2. select 1 as id, 12 as start_time, 15 as end_time
    3. union all
    4. select 2 as id, 57 as start_time, 58 as end_time
    5. union all
    6. select 3 as id, 29 as start_time, 32 as end_time
    7. union all
    8. select 4 as id, 30 as start_time, 31 as end_time
    9. union all
    10. select 5 as id, 17 as start_time, 19 as end_time
    11. union all
    12. select 6 as id, 44 as start_time, 44 as end_time
    13. union all
    14. select 7 as id, 56 as start_time, 57 as end_time
    15. union all
    16. select 8 as id, 16 as start_time, 18 as end_time

    2 数据分析

    解题要点:如何判断哪些区间是要合并的?

    其实换个角度就是哪些区间是交叉的,哪些是重复的?

    判断思路:如果将起始时间,和结束时间进行排序,当前行的起始时间小于等于上一行的结束时间,那么日期就存在交叉,存在重复的数据。根据该条件我们可以设置断点,然后用经典的思路sum() over()来获取分组id,问题便得到解决。

    第一步:按照起始时间和结束时间进行降序排序,获取上一行的结束时间,目的是为了比较

    1. select id,
    2. start_time,
    3. end_time,
    4. lag(end_time, 1, end_time) over (order by start_time asc, end_time asc) as lga_end_time
    5. from test02

     

    第二步:根据lag_end_time进行判断,当当前行的start_time <=  lag_end_time时候设置标记值0,否则为1(经典的按条件变化后的分组思路,这里一定是满足条件的时候置为0,不满足条件的时候置为1)

    1. select id
    2. , start_time
    3. , end_time
    4. , case when start_time <= lga_end_time then 0 else 1 end as flg --条件成立的时候为0,不成立的时候为1
    5. from (select id,
    6. start_time,
    7. end_time,
    8. lag(end_time, 1, end_time) over (order by start_time asc, end_time asc) as lga_end_time
    9. from test02

     第三步:按照sum() over()的方法获取分组id

    1. select id
    2. , start_time
    3. , end_time
    4. , sum(flg) over (order by start_time, end_time ) as grp_id
    5. from (select id
    6. , start_time
    7. , end_time
    8. , case when start_time <= lga_end_time then 0 else 1 end as flg --条件成立的时候为0,不成立的时候为1
    9. from (select id,
    10. start_time,
    11. end_time,
    12. lag(end_time, 1, end_time) over (order by start_time asc, end_time asc) as lga_end_time
    13. from test02
    14. ) t
    15. ) t

     第四步:在分组里获取,最小值及最大值,最小值即为起始点,最大值即为结束点,分组id即为id

    最终的SQL如下:

    1. select grp_id + 1 as id
    2. , min(start_time) as start_time
    3. , max(end_time) as end_time
    4. from (
    5. select id
    6. , start_time
    7. , end_time
    8. , sum(flg) over (order by start_time, end_time ) as grp_id
    9. from (select id
    10. , start_time
    11. , end_time
    12. , case when start_time <= lga_end_time then 0 else 1 end as flg --条件成立的时候为0,不成立的时候为1
    13. from (select id,
    14. start_time,
    15. end_time,
    16. lag(end_time, 1, end_time) over (order by start_time asc, end_time asc) as lga_end_time
    17. from test02
    18. ) t
    19. ) t
    20. ) t
    21. group by grp_id

     2 小结

       本题为区间合并问题,问题比较经典,判断的核心思路是构造条件:

    当前行的起始时间<=上一行的结束时间(按照起始时间和结束时间降序排序)。

    然后利用经典的分组思路,在一个分组中求最小、最大值即为所求。

  • 相关阅读:
    RHCSA-VM-Linux安装虚拟机后的基础命令
    二十一、操作系统设计(POSIX;Windows API;Micro/Exo/Unikernel)
    Vue08 事件的基本使用
    数据库的备份和恢复
    5G及其后的5G非地面网络:趋势和研究挑战-HARQ部分
    被vector动态扩容给坑了!
    (附源码)计算机毕业设计SSM基于的扶贫产品展销平台
    又拍云 Redis 的改进之路
    架构基本概念和架构本质
    基于Java微信小程序火锅店点餐系统设计和实现(源码+LW+调试文档+讲解等)
  • 原文地址:https://blog.csdn.net/godlovedaniel/article/details/126662876