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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |