当前位置: 首页 > 面试题库 >

多个日期范围之间的分钟总和

东郭鹤龄
2023-03-14
问题内容

场景是 用户 指定何时 可用 ,这些指定的时间可以 彼此重叠 。我正在尝试获取可用的总时间。SQL Fiddle示例:

--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小提琴

有什么想法可以实现这一目标吗?

编辑11月21日,11:到目前为止,感谢每个人的解决方案-从某种程度上来说,我很高兴看到这并不是编写一个“简单”的查询。

编辑11月23日 :一切都是伟大的工作。在内部,我们认为最好是确保用户不能输入重叠的时间(例如,强迫他们修改现有的输入)!


问题答案:

Gordon Linoff有基于CTE的答案

我已经对所有可用算法进行了性能分析。空白值表示此过程花费了太长时间。这是在单个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, 0, AvailStart, 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, CurStart, 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,
    AvailStart,
    AvailEnd
  From
    dbo.Available
  Where
    UserID = @UserID
)
Insert Into @UserRanges Select * From OrderedRanges


;With AccumulateMinutes (RN,Accum, CurStart, CurEnd) as (
  Select
    RN, 0, AvailStart, AvailEnd
  From
    @UserRanges
  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 
    @UserRanges o On 
        a.RN + 1 = o.RN
)

Select 
  @Ret = Max(Accum + datediff(Minute, CurStart, CurEnd)) 
From 
  AccumulateMinutes 
Option
  (MaxRecursion 0)

Return @Ret

End

http://sqlfiddle.com/#!6/bfd94



 类似资料:
  • 问题内容: 我的数据库中有以下一组匹配日期的日期(dd / MM / yyyy): 事件具有开始和结束日期(时间无关紧要),并且endDate为NULL表示事件仍在进行中。 我想确定的是两个任意日期之间的日期范围,其中a)没有事件,b)事件重叠。 因此,对于输入日期范围01/04/2009-30/06/2009,我希望得到以下结果: 注意,作为结果,两个相邻的重叠范围是可以接受的。 谁能用SQL算

  • 问题内容: 我需要填充一个表,该表将存储2个给定日期之间的日期范围:09/01/11-10/10/11 因此,在这种情况下,该表将从2011年9月1日开始存储,直到每天存储到10/10/11,我想知道在SQL Server中是否有一种巧妙的方法-我目前正在使用SQL Server 2008 。 谢谢 问题答案: 在SQL 2005+上很容易;如果您有数字表或理货表,操作会更容易。我在下面伪造了它:

  • 我有一张表,上面有房间的开始和结束日期。我写了这个SQL: 这些房间在这段时间内是有效的。但是,当我这样过滤的时候, 我没有结果。这些日期就像一个数组: 所以,你可以看到那个房间在2022-12-30是活动的,但我不能得到它。我怎么能那样做? 希望我能很好地表达自己。

  • 问题内容: 如果我有两个日期,我该如何使用JavaScript以分钟为单位获取两个日期之间的差额? 问题答案: 您可以签出以下代码: 或舍弃秒,如果您不想舍入分钟。

  • 问题内容: 我想在JCalendar上禁用多个日期范围。我正在按照以下步骤操作,但是我需要知道如何添加多个日期评估器。请帮助我,谢谢。 更新:我不知道为什么我不能禁用JCalendar上的日期。我将把代码留在这里,以便大家检查。 这是我的RangeEvaluator类,从该类修改而来。 这是我使用RangeEvaluator类的方式: 我想念什么吗?请帮助我,谢谢。 问题答案: 根据您的更新,这里

  • 问题内容: 给定以下日期: 和一个静态变量: 我需要创建一个数组,如: 周末除外。 不,这不是功课…出于某种原因,我今天无法直截了当。 问题答案: 对于PHP> = 5.3.0,请使用DatePeriod类。不幸的是,几乎没有记录。