sql – 多个日期范围之间的分钟总和
|
该方案是用户指定何时可用,这些指定时间可以相互重叠.我正在努力获得他们可用的总时间. SQL小提琴示例: --Available-- ID userID availStart availEnd 1 456 '2012-11-19 16:00' '2012-11-19 17:00' 2 456 '2012-11-19 16:00' '2012-11-19 16:50' 3 456 '2012-11-19 18:00' '2012-11-19 18:30' 4 456 '2012-11-19 17:30' '2012-11-19 18:10' 5 456 '2012-11-19 16:00' '2012-11-19 17:10' 6 456 '2012-11-19 16:00' '2012-11-19 16:50' 输出应为130分钟: 1: 60 2: 0 as falls inside 1 3: 30 4: 30 as the last 10 mins is covered by 3 5: 10 as first 60 mins is covered by 1 6: 0 as falls inside 1 我可以获得总重叠分钟数,但这超过了可用分钟数的总和: SQL Fiddle 我有什么想法可以达到这个目的吗? 编辑11月21日:感谢所有人的解决方案 – 在某种程度上,我很高兴看到这不是一个’简单’的查询. 编辑11月23日12:这都是伟大的工作.在内部,我们认为最好确保用户不能输入重叠时间(例如强制他们修改现有条目)! 解决方法Gordon Linoff有一个 CTE based answer我在所有工作算法上做了一些performance analysis 这个CTE版本比线性更差,SQL Server无法以有效的方式做RN = RN 1加入.我使用下面的混合方法对此进行了纠正,我将第一个CTE保存并索引到表变量中.这仍然是基于光标的方法的IO的十倍. With OrderedRanges as (
Select
Row_Number() Over (Partition By UserID Order By AvailStart) AS RN,AvailStart,AvailEnd
From
dbo.Available
Where
UserID = 456
),AccumulateMinutes (RN,Accum,CurStart,CurEnd) as (
Select
RN,AvailEnd
From
OrderedRanges
Where
RN = 1
Union All
Select
o.RN,a.Accum + Case When o.AvailStart <= a.CurEnd Then
0
Else
DateDiff(Minute,a.CurStart,a.CurEnd)
End,Case When o.AvailStart <= a.CurEnd Then
a.CurStart
Else
o.AvailStart
End,Case When o.AvailStart <= a.CurEnd Then
Case When a.CurEnd > o.AvailEnd Then a.CurEnd Else o.AvailEnd End
Else
o.AvailEnd
End
From
AccumulateMinutes a
Inner Join
OrderedRanges o On
a.RN = o.RN - 1
)
Select Max(Accum + datediff(Minute,CurEnd)) From AccumulateMinutes
http://sqlfiddle.com/#!6/ac021/2 在做了一些性能分析之后,这里是一个混合的CTE /表变量版本,除了基于游标的方法之外,它的性能要好于任何东西 Create Function dbo.AvailMinutesHybrid(@UserID int) Returns Int As
Begin
Declare @UserRanges Table (
RN int not null primary key,AvailStart datetime,AvailEnd datetime
)
Declare @Ret int = Null
;With OrderedRanges as (
Select
Row_Number() Over (Partition By UserID Order By AvailStart) AS RN,AvailEnd
From
dbo.Available
Where
UserID = @UserID
)
Insert Into @UserRanges Select * From OrderedRanges
;With AccumulateMinutes (RN,AvailEnd
From
@UserRanges
Where
RN = 1
Union All
Select
o.RN,Case When o.AvailStart <= a.CurEnd Then
Case When a.CurEnd > o.AvailEnd Then a.CurEnd Else o.AvailEnd End
Else
o.AvailEnd
End
From
AccumulateMinutes a
Inner Join
@UserRanges o On
a.RN + 1 = o.RN
)
Select
@Ret = Max(Accum + datediff(Minute,CurEnd))
From
AccumulateMinutes
Option
(MaxRecursion 0)
Return @Ret
End
http://sqlfiddle.com/#!6/bfd94 (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
