sql - 重叠时间间隔 : Select "all busy" periods

标签 sql sql-server intervals gaps-and-islands sql-server-2014

我尝试仅使用 SQL 从分组时间间隔集中选择常见的重叠时间间隔(或周期,这可能是正确的词)。

现实世界的场景是一个具有 3 个以上可以接听电话的位置的调用中心。职位由特定的服务代表填补,他们的职位分配会随着时间的推移而变化,但这与这个问题无关。我们可以假设对于给定的职位,它总是由某人填补。

职位数量随着时间的推移而缓慢变化。我正在尝试概括该解决方案,以便它可以处理任意数量的位置。

输入数据是一组调用,这些调用定向到某个位置并具有开始时间和结束时间。显然,给定位置不能与其自身有重叠的调用(假设一次只能接听一个调用),但其调用可以在时间上与对其他位置的一个或多个调用重叠。

问题是从调用数据中识别所有位置都在通话的所有时间间隔,因此调用中心无法应答该时间段内的任何新来电(“所有位置都忙”)。

例如,对于三个位置(编号为 1, 2 3)

Call  Position  CallStartTime      CallEndTime

1     1         2014-01-01 14:01   2014-01-01 14:33     <--Comprises all busy intervals 1 and 2
2     1         2014-01-01 14:45   2014-01-01 14:47  
3     1         2014-01-01 14:53   2014-01-01 14:57  
4     2         2014-01-01 13:01   2014-01-01 13:53    
5     2         2014-01-01 13:55   2014-01-01 14:25     <--comprises all busy interval 1
6     2         2014-01-01 14:27   2014-01-01 14:29     <--comprises all busy interval 2
7     2         2014-01-01 14:35   2014-01-01 14:41  
8     3         2014-01-01 14:21   2014-01-01 15:03     <--comprises all busy intervals 1 and 2
9     3         2014-01-01 16:01   2014-01-01 16:11

对于上面的测试数据,所有位置都繁忙时有两个时间间隔(所有位置重叠调用的明显情况):14:21 - 14:25 和 14:27 - 14:29。

所以期望的结果集是

AllBusyStartTime  AllBusyEndTime
2014-01-01 14:21  2014-01-01 14:25
2014-01-01 14:27  2014-01-01 14:29 

您会看到,一个调用可以与其他调用有多个重叠(例如,调用位置 1 14:01-14:33 与调用位置 2 13:55-14:25 和调用位置 2 14:27-14 都重叠) :29)。

当 a.StartTime < b.EndTime 且 a.EndTime >= b.StartTime 时,两个时间间隔 (a, b) 重叠。

如果我可以获得所有位置都有重叠的调用时间间隔集,则关联的“全忙”时间间隔由该集合中最大(最近)的开始时间和最小(最旧)的结束时间组成。

为了更接近解决方案,我正在寻找一种通用算法来确定 n 个时间间隔何时相互重叠。对于区间a、b、c,选择a 重叠b 和a 重叠c 的限制性不够。 A 可以与 b 重叠,但 b 可能不会与 c 重叠,并且您需要所有间隔都彼此重叠。

我正在使用 SQL Server 进行测试。我尝试在网上搜索,但没有找到任何完全涵盖这种情况的内容(关于两个重叠时间间隔的简单情况的大量讨论)。我会分享 SQL,但我仍在尝试找出“方法”,这是需要它来说明的。

即使我只有 SQL-Server 进行测试,我也希望解决方案尽可能通用,因为它可能不会在 SQL Server 上实现。

最佳答案

让我们将其视为随时获取同时调用的数量。该方法是获取时间列表,+1 表示调用开始,-1 表示调用结束。下面给出了每个时间段的计数:

select thetime, sum(incall) over (order by thetime, call) as simultaneouscalls
from ((select CallStartTime as thetime, call, +1 as incall
       from calls
      ) union all
      (select CallEndTime, call, -1 as incall
       from calls
      )
     ) c;

接下来,您需要周期,因此使用 lead() 获取周期的结束时间,然后按同时调用的数量排序:

with c as (
      select thetime, sum(incall) over (order by thetime, call) as simultaneouscalls
      from ((select CallStartTime as thetime, call, +1 as incall
             from calls
            ) union all
            (select CallEndTime, call, -1 as incall
             from calls
            )
           ) c
    )
select thetime, endtime, simultaneouscalls
from (select c.*, lead(thetime) over (order the thetime) as endtime
      from c
     ) c
order by simultaneouscalls, thetime;

如果您确实只想要最大值,请将此 where 子句添加到外部查询:

where simultaneouscalls = (select count(distinct position) from calls)

注意:这使用 SQL Server 2012+ 中可用的结构,但在早期版本中不可用(当我写这篇文章时,没有任何版本指示)。

关于sql - 重叠时间间隔 : Select "all busy" periods,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26577322/

相关文章:

python - SQLAlchemy : Get database name from engine

sql-server - 更改无 DSN Access 前端的 SQL 连接信息

sql - 创建存储过程

mysql - 以 -15 分钟间隔选择日期时间显示错误结果

sql - 窗口函数解决复杂的日期操作

c# - 如何从linq中的子表中选择列

sql - 找到给定时间序列之间最近的重叠

Javascript:以所需的时间间隔执行2个函数

c# - 列 <列名> 不属于表

mysql - 如何选择从上一小时到去年之间的行?