该方案是用户指定何时可用,这些指定时间可以相互重叠.我正在努力获得他们可用的总时间. 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
我可以获得总重叠分钟数,但这超过了可用分钟数的总和:
我有什么想法可以达到这个目的吗?
解决方法
Gordon Linoff有一个
CTE based answer
我在所有工作算法上做了一些performance analysis
空白值意味着花了太长时间.这是在单个Core i7 X920 @ 2GHz芯片上测试的,由几个SSD支持.创建的唯一索引是UserID上的集群,AvailStart.如果您认为可以改善任何表现,请告诉我.
这个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