这可能过于复杂,但它确实有效。我们使用许多 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 计算这两个时间点之间的每晚。然后我们将其加入到原始表(in Overlaps
)中,以找到每晚适用的那些时段。最后,在 中Totals
,我们计算每个重叠时段贡献了多少小时。
这应该适用于多日活动。Totals
如果您需要计算部分小时数,您可能希望将 CTE 更改为使用分钟,或应用一些其他舍入函数。