这可能过于复杂,但确实有效。我们使用许多 CTE 来构建有用的中间表示:
declare @Times table (
ID int not null,
StartTime datetime not null,
EndTime datetime not null
)
insert into @Times (ID,StartTime,EndTime)
select 1,'2012-07-04T05:00:00.000','2012-07-04T23:00:00.000' union all
select 2,'2012-07-04T18:00:00.000','2012-07-05T05:00:00.000'
;With Start as (
select MIN(DATEADD(day,DATEDIFF(day,0,StartTime),0)) as StartDay from @Times
), Ends as (
select MAX(EndTime) EndTime from @Times
), Nights as (
select DATEADD(hour,-2,StartDay) as NightStart,DATEADD(hour,6,StartDay) as NightEnd from Start
union all
select DATEADD(DAY,1,NightStart),DATEADD(DAY,1,NightEnd) from Nights n
inner join Ends e on n.NightStart < e.EndTime
), Overlaps as (
select
t.ID,
CASE WHEN n.NightStart > t.StartTime THEN n.NightStart ELSE t.StartTime END as StartPeriod,
CASE WHEN n.NightEnd < t.EndTime THEN n.NightEnd ELSE t.EndTime END as EndPeriod
from
@Times t
inner join
Nights n
on
t.EndTime > n.NightStart and
t.StartTime < n.NightEnd
), Totals as (
select ID,SUM(DATEDIFF(hour,StartPeriod,EndPeriod)) as TotalHours
from Overlaps
group by ID
)
select
*
from
@Times t
inner join
Totals tot
on
t.ID = tot.ID
结果:
ID StartTime EndTime ID TotalHours
----------- ----------------------- ----------------------- ----------- -----------
1 2012-07-04 05:00:00.000 2012-07-04 23:00:00.000 1 2
2 2012-07-04 18:00:00.000 2012-07-05 05:00:00.000 2 7
您会注意到,我必须添加一个 ID 列才能使我的关联起作用。
Start CTE 查找最早的适用午夜。 End CTE 查找我们需要查找重叠夜晚的最后时间。然后,递归的Nights CTE 每天晚上在这两个时间点之间进行计算。然后,我们将其加入到原始表(Overlaps)中,以查找每晚适用的时段。最后,在Totals 中,我们计算每个重叠时段贡献了多少小时。
这应该适用于多日活动。如果您需要计算部分小时数,您可能希望将 Totals CTE 更改为使用分钟,或应用其他一些舍入函数。