Со ссылкой на SQL-запросы для перекрывающихся периодов времени на SQL Server и на их основе ...
При сравнении двух периодов времени T1 и T2 существует пять возможных вариантов:
- T1 и T2 не пересекаются, они не перекрываются.
- T1 полностью охватывает T2.
- T2 полностью охватывает T1.
- T1 перекрываетначало T2.
- T2 перекрывает начало T1.
Это составляется, когда T3 вводится и может перекрывать любой, все или ни один из T1 и T2.
Начиная с данных вашего примера:
declare @Durations table (
[Group] int not null,
FromDate datetime not null,
ToDate datetime not null
);
insert @Durations values
(1, '2019-09-30 11:13:00', '2019-09-30 11:13:50'),
(1, '2019-09-30 11:13:20', '2019-09-30 11:14:10'),
(2, '2019-09-30 11:20:00', '2019-09-30 11:20:20'),
(1, '2019-09-30 11:20:10', '2019-09-30 11:20:20'),
(3, '2019-09-30 11:25:00', '2019-09-30 11:25:30');
select * from @Durations;
Group FromDate ToDate
----------- ----------------------- -----------------------
1 2019-09-30 11:13:00.000 2019-09-30 11:13:50.000
1 2019-09-30 11:13:20.000 2019-09-30 11:14:10.000
2 2019-09-30 11:20:00.000 2019-09-30 11:20:20.000
1 2019-09-30 11:20:10.000 2019-09-30 11:20:20.000
3 2019-09-30 11:25:00.000 2019-09-30 11:25:30.000
Мы можем сгруппировать хронологии, идентифицируя перекрывающиеся периоды времени, назначая перекрывающиеся периоды одному периоду времени ...
;with Chronologies as (
select [Group],
FromDate,
ToDate,
Chronology = row_number() over (partition by [Group] order by FromDate, ToDate)
from @Durations
), CTE as (
select [Group], FromDate, ToDate, Chronology, 1 as Span
from Chronologies
where Chronology = 1
union all
select p2.[Group],
p2.FromDate,
p2.ToDate,
p2.Chronology,
Span = case when
(p1.FromDate between p2.FromDate and p2.ToDate) or
(p1.ToDate between p2.FromDate and p2.ToDate) or
(p1.FromDate < p2.FromDate and p1.ToDate > p2.ToDate) or
(p1.FromDate > p2.FromDate and p1.ToDate < p2.ToDate)
then p1.Span else (1 + p1.Span) end
from CTE p1
inner join Chronologies p2 on p2.[Group]=p1.[Group] and p2.Chronology=(1 + p1.Chronology)
)
select *
from CTE
order by [Group], Chronology;
Group FromDate ToDate Chronology Span
----------- ----------------------- ----------------------- -------------------- -----------
1 2019-09-30 11:13:00.000 2019-09-30 11:13:50.000 1 1
1 2019-09-30 11:13:20.000 2019-09-30 11:14:10.000 2 1
1 2019-09-30 11:20:10.000 2019-09-30 11:20:20.000 3 2
2 2019-09-30 11:20:00.000 2019-09-30 11:20:20.000 1 1
3 2019-09-30 11:25:00.000 2019-09-30 11:25:30.000 1 1
Мы можем использовать столбец Span для объединения периодов времени группы, то есть: group by [Group], Span
позволяет нам использовать min(FromDate)
и max(ToDate)
для вычисления продолжительности данного периода с помощью datediff()
, и мы можем sum()
эти длительности, чтобы достичь вашего DurationTime
результата ...
;with Chronologies as (
select [Group],
FromDate,
ToDate,
Chronology = row_number() over (partition by [Group] order by FromDate, ToDate)
from @Durations
), CTE as (
select [Group], FromDate, ToDate, Chronology, 1 as Span
from Chronologies
where Chronology = 1
union all
select p2.[Group],
p2.FromDate,
p2.ToDate,
p2.Chronology,
Span = case when
(p1.FromDate between p2.FromDate and p2.ToDate) or
(p1.ToDate between p2.FromDate and p2.ToDate) or
(p1.FromDate < p2.FromDate and p1.ToDate > p2.ToDate) or
(p1.FromDate > p2.FromDate and p1.ToDate < p2.ToDate)
then p1.Span else (1 + p1.Span) end
from CTE p1
inner join Chronologies p2 on p2.[Group]=p1.[Group] and p2.Chronology=(1 + p1.Chronology)
)
select [Group], DurationTime = sum(datediff(second, FromDate, ToDate))
from (
select [Group], Span, FromDate=min(FromDate), ToDate=max(ToDate)
from CTE
group by [Group], Span
) Coalesced
group by [Group]
order by [Group];
Что дает нам окончательный результат:
Group DurationTime
----------- ------------
1 80
2 20
3 30