TSQL get overlapping periods from datetime ranges

限于喜欢 提交于 2019-12-24 17:19:41

问题


I have a table with date range an i need the sum of overlapping periods (in hours) between its rows.

This is a schema example:

create table period (
    id int,
    starttime datetime,
    endtime datetime,
    type varchar(64)
  );

insert into period values (1,'2013-04-07 8:00','2013-04-07 13:00','Work');
insert into period values (2,'2013-04-07 14:00','2013-04-07 17:00','Work');
insert into period values (3,'2013-04-08 8:00','2013-04-08 13:00','Work');
insert into period values (4,'2013-04-08 14:00','2013-04-08 17:00','Work');
insert into period values (5,'2013-04-07 10:00','2013-04-07 11:00','Holyday'); /* 1h overlapping with 1*/
insert into period values (6,'2013-04-08 10:00','2013-04-08 20:00','Transfer'); /* 6h overlapping with 3 and 4*/
insert into period values (7,'2013-04-08 11:00','2013-04-08 12:00','Test');  /* 1h overlapping with 3 and 6*/

And its fiddle: http://sqlfiddle.com/#!6/9ca31/10

I expect a sum of 8h overlapping hours: 1h (id 5 over id 1) 6h (id 6 over id 3 and 4) 1h (id 7 over id 3 and 6)

I check this: select overlapping datetime events with SQL but seems to not do what I need.

Thank you.


回答1:


select sum(datediff(hh, case when t2.starttime > t1.starttime then t2.starttime else t1.starttime end,
    case when t2.endtime > t1.endtime then t1.endtime else t2.endtime end))
from period t1 
join period t2 on t1.id < t2.id
where t2.endtime > t1.starttime and t2.starttime < t1.endtime;

Updated to handle several overlaps:

select sum(datediff(hh, start, fin))
from (select distinct
case when t2.starttime > t1.starttime then t2.starttime else t1.starttime end as start,
case when t2.endtime > t1.endtime then t1.endtime else t2.endtime end as fin
from period t1 
join period t2 on t1.id < t2.id
where t2.endtime > t1.starttime and t2.starttime < t1.endtime
) as overlaps;



回答2:


I have some "dirty" solution. Hope this helps :)

with src as (
    select
     convert(varchar, starttime, 112) [start_date]
    , cast(left(convert(varchar, starttime, 108), 2) as int) [start_time]
    , convert(varchar, endtime, 112) [end_date]
    , cast(left(convert(varchar, endtime, 108), 2) as int) [end_time]
    , id
    from [period]),

[gr] as (
    select
    row_number() over(order by s1.[start_date], s1.[start_time], s1.[end_time], s2.[start_time], s2.[end_time]) [no]
    , s1.[start_date] [date]
    , s1.[start_time] [t1]
    , s1.[end_time] [t2]
    , s2.[start_time] [t3]
    , s2.[end_time] [t4]
    from src s1
    join src s2 on s1.[start_date] = s2.[start_date]
        and s1.[end_date] = s2.[end_date]
        and (s1.[start_time] between s2.[start_time] and s2.[end_time] or s1.[end_time] between s2.[start_time] and s2.[end_time])
        and s1.id != s2.id),

[raw] as (
    select [no], [date], [t1] [h] from [gr] union all
    select [no], [date], [t2] from [gr] union all
    select [no], [date], [t3] from [gr] union all
    select [no], [date], [t4] from [gr]),

[max_min] as (
    select [no], [date], max(h) [max_h], min(h) [min_h]
    from [raw]
    group by [no], [date]
),

[result] as (
    select [raw].*
    from [raw]
    left join [max_min] on [raw].[no] = [max_min].[no]
        and ([raw].h = [max_min].[max_h] or [raw].h = [max_min].[min_h])
    where [max_min].[no] is null),

[final] as (
    select distinct r1.[date], r1.h [start_h], r2.h [end_h], abs(r1.h - r2.h) [dif]
    from [result] r1
    join [result] r2 on r1.[no] = r2.[no]
    where abs(r1.h - r2.h) > 0
    and r1.h > r2.h)

select sum(dif) [overlapping hours] from [final]

SQLFiddle



来源:https://stackoverflow.com/questions/15873116/tsql-get-overlapping-periods-from-datetime-ranges

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!