TSQL获得唯一(不重叠)日期时间范围

时间:2013-04-16 08:20:31

标签: tsql datetime overlapping

这是一个类似的问题:TSQL get overlapping periods from datetime ranges但是有不同的结果请求。

这是表格:

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

我需要唯一不重叠日期时间范围表。

在前面的示例中,结果将是:

'2013-04-07 08:00','2013-04-07 13:00'
'2013-04-07 14:00','2013-04-07 17:00'
'2013-04-08 08:00','2013-04-08 20:00'

如果可能是时间碎片,则不是很重要,例如:

'2013-04-08 08:00','2013-04-08 13:00'
'2013-04-08 12:00','2013-04-08 20:00'

- 编辑 -

另一个例子:

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

insert into period values (1,'2013-06-13 8:30','2013-06-13 12:30','');
insert into period values (2,'2013-06-13 8:38','2013-06-13 12:38','');
insert into period values (3,'2013-06-13 13:18','2013-06-13 17:45','');
insert into period values (4,'2013-06-13 13:30','2013-06-13 17:30','');
insert into period values (5,'2013-06-13 20:00','2013-06-13 23:59','');

这应该返回:

2013-06-13 08:30 - 2013-06-13 12:38

2013-06-13 13:18 - 2013-06-13 17:45

2013-06-13 20:00 - 2013-06-13 23:59

2 个答案:

答案 0 :(得分:2)

但是你只有一个非重叠的时期,或者我是否理解错误的问题?

select * 
from period t
where id in (
select t1.id
from period t1 
join period t2 on t1.id <> t2.id
where t2.endtime <= t1.starttime or t2.starttime >= t1.endtime
group by t1.id
having count(*) + 1 = (select count(*) from period)
) 

结果:

'2013-04-07 14:00','2013-04-07 17:00'

更新:好的,您希望合并重叠范围。试试这个:

select starttime, endtime 
from period 
where id in (
select t1.id
from period t1 
join period t2 on t1.id <> t2.id
where t2.endtime < t1.starttime or t2.starttime > t1.endtime
group by t1.id
having count(*) + 1 = (select count(*) from period)
) 

union all

select min(start), max(fin) from (
select 
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) overlaps
group by datepart(dd, start), datepart(dd, fin)

答案 1 :(得分:0)

我找到了这个解决方案......我认为这不是最好的方法,但似乎有效。

DECLARE @union_unique TABLE (id INT IDENTITY(1, 1) primary key ,starttime datetime,endtime datetime)
DECLARE @idset TABLE (id int)

DECLARE @i int


SET @i = 1
IF (SELECT COUNT(*) FROM period) > 0
    WHILE (@i <= (SELECT MAX(id) FROM period))
       BEGIN

          delete from @idset
          insert into @idset
          select distinct t2.id
          from period t1
          join @union_unique t2 on convert(date, t1.starttime)=convert(date, t2.starttime)
          where t1.id=@i and
          (
            t1.starttime >= t2.starttime and t1.starttime <= t2.endtime 
            or
            t1.endtime >= t2.starttime and t1.endtime <= t2.endtime 
            or
            t1.starttime <= t2.starttime and t1.endtime >= t2.endtime 
           )



          if(select count(*) from @idset)=0
            insert into @union_unique (starttime, endtime) select starttime, endtime from period where id=@i
          else
            BEGIN


              insert into @union_unique (starttime, endtime)
                select 
                    min(starttime),
                    max(endtime)
                from (
                    select starttime, endtime from @union_unique where id in (select id from @idset)
                    union 
                    select starttime, endtime from period where id=@i
                ) alll

              delete from @union_unique where id in (select id from @idset)

            END

          SET @i = @i + 1


    END


select * from @union_unique order by starttime