2013-04-08 104 views
1

我有一个表与日期范围我需要其行之间的重叠时期(以小时为单位)的总和。TSQL从日期时间范围获得重叠期

这是一个模式例如:

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*/ 

而其拨弄:http://sqlfiddle.com/#!6/9ca31/10

我期望的8H重叠小时的总和: 1H(ID 5以上的id 1) 6H(ID 6以上编号3和4) 1h(id 7 over id 3 and 6)

我检查了这个:select overlapping datetime events with SQL但似乎没有做我所需要的。

谢谢。

回答

2
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; 

更新处理多个重叠:

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; 
+0

有一个bug ......尝试添加另一个重叠记录:插入周期值(7,'2013-04-08 10:00','2013-04-08 11:00','测试');我预计只有另外1小时的重叠总和。 – Tobia 2013-04-09 08:22:55

+0

我认为这个查询需要一个cte表达式 – Tobia 2013-04-09 08:38:28

+0

这不能处理所有情况,例如:339989 \t 2015-01-20 00:00:00 \t 2015-05-10 00:00:00 2015-01 -20 00:00:00 \t 2015-08-19 00:00:00 2015-01-20 00:00:00 \t 2015-08-19 00:00:00 2015-05-11 00:00:00 \t 2015-08-19 00:00:00 2015-08-20 00:00:00 \t 2015-12-06 00:00:00 2015-08-20 00: 00:00 \t 2015-12-06 00:00:00 – 2016-06-06 20:41:27

0

我有一些“脏”的解决方案。希望这有助于:)

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