Assuming some reasonable recent version of SQL Server, this ought to be a good start:
-- Some sample data.
declare @Samples as Table ( SampleId Int Identity, Start DateTime, Stop DateTime );
insert into @Samples ( Start, Stop ) values
( '2/2/2015 7:00', '2/2/2015 7:25' ),
( '2/2/2015 7:20', '2/2/2015 7:29' ),
( '2/2/2015 7:35', '2/2/2015 7:42' ),
( '2/2/2015 8:05', '2/2/2015 8:14' ),
( '2/2/2015 8:16', '2/2/2015 8:20' ),
( '2/2/2015 8:29', '2/2/2015 8:40' ),
( '2/2/2015 8:55', '2/2/2015 9:25' );
select * from @Samples;
-- Find the limits and align them to quarter hours.
declare @Min as DateTime;
declare @Max as DateTime;
select @Min = min( Start ), @Max = max( Stop )
from @Samples;
set @Min = DateAdd( minute, -DatePart( minute, @Min ) % 15, @Min );
set @Max = DateAdd( minute, 15 - DatePart( minute, @Max ) % 15, @Max );
select @Min as [Min], @Max as [Max];
-- Go for it.
with QuarterHours ( QuarterStart, QuarterStop )
as (
select @Min, DateAdd( minute, 15, @Min )
union all
select QuarterStop, DateAdd( minute, 15, QuarterStop )
from QuarterHours
where QuarterStop < @Max ),
Overlaps
as ( select QH.QuarterStart, QH.QuarterStop, S.Start, S.Stop,
case
when S.Start <= QH.QuarterStart and S.Stop >= QH.QuarterStop then 15
when S.Start <= QH.QuarterStart and S.Stop < QH.QuarterStop then DateDiff( minute, QH.QuarterStart, S.Stop )
when S.Start > QH.QuarterStart and S.Stop >= QH.QuarterStop then DateDiff( minute, S.Start, QH.QuarterStop )
when S.Start > QH.QuarterStart and S.Stop < QH.QuarterStop then DateDiff( minute, S.Start, S.Stop )
else 0 end as Overlap
from QuarterHours as QH left outer join
@Samples as S on S.Start <= QH.QuarterStop and S.Stop >= QH.QuarterStart )
select QuarterStart, sum( Overlap ) as [ActivityTime]
from Overlaps
group by QuarterStart
order by QuarterStart;
You can change the last select
to either select * from QuarterHours
or select * from Overlaps
to see some of the intermediate values.
Explanatory notes:
You can use any range (@Min
/@Max
) you want, I just took them from the sample data so that the example would run. I used a table variable for the same reason, no need to create a "real" table for the sake of an example.
The Common Table Expression (CTE) creates, via recursion, a table of QuarterHours
that covers the desired range. (A numbers table or tally table could also be used to generate the quarter hours.) Then a LEFT OUTER JOIN
with the sample data is used to locate all of the Overlaps
, if any, with each quarter hour. That preserves the quarter hours for which there is no activity.
The final SELECT
summarizes the results.