在不同时间间隔中查找数据库中的同步事件

nww*_*son 15 sql sql-server

我有一个存储电话记录的数据库.每个电话记录都有开始时间和结束时间.我想知道同时发生的最大电话数是多少,以便知道我们是否超过了手机银行中可用电话线的数量.我怎么能解决这个问题呢?

Unr*_*son 10

鉴于最大连接数将是StartTime点,您可以

SELECT TOP 1 count(*) as CountSimultaneous
FROM PhoneCalls T1, PhoneCalls T2
WHERE
     T1.StartTime between T2.StartTime and T2.EndTime
GROUP BY
     T1.CallID
ORDER BY CountSimultaneous DESC
Run Code Online (Sandbox Code Playgroud)

查询将为每个呼叫返回同时呼叫的数量.要么命令它们下降并从上面选择第一个或SELECT MAX(CountSimultaneous)(作为子查询而不排序和没有TOP).


Qse*_*bas 8

免责声明:我正在根据(优秀)以下帖子撰写我的答案:

http://sqlmag.com/t-sql/calculating-concurrent-sessions-part-3(也推荐第1部分和第2部分)

这个问题首先要理解的是,互联网上发现的大多数当前解决方案基本上都有两个问题

  • 结果不是正确答案(例如,如果范围A与B和C重叠但B不与C重叠,则它们计为3个重叠范围).
  • 计算它的方式是非常低效的(因为是O(n ^ 2)和/或它们在该时期中每秒钟的cicle)

像Unreasons提出的解决方案中的常见性能问题是一个cuadratic解决方案,对于每个调用,如果它们被重叠,您需要检查所有其他调用.

有一个算法线性公共解决方案,列出按日期排序的所有"事件"(开始调用和结束调用),并为开始添加1,为挂起添加1,并记住最大值.这可以通过游标轻松实现(Hafhor提出的解决方案似乎就是这样)但游标并不是解决问题的最有效方法.

所引用的文章有很好的例子,不同的解决方案,它们的性能比较.建议的解决方案是:

WITH C1 AS
(
  SELECT starttime AS ts, +1 AS TYPE,
    ROW_NUMBER() OVER(ORDER BY starttime) AS start_ordinal
  FROM Calls

  UNION ALL

  SELECT endtime, -1, NULL
  FROM Calls
),
C2 AS
(
  SELECT *,
    ROW_NUMBER() OVER(  ORDER BY ts, TYPE) AS start_or_end_ordinal
  FROM C1
)
SELECT MAX(2 * start_ordinal - start_or_end_ordinal) AS mx
FROM C2
WHERE TYPE = 1
Run Code Online (Sandbox Code Playgroud)

说明

假设这组数据

+-------------------------+-------------------------+
|        starttime        |         endtime         |
+-------------------------+-------------------------+
| 2009-01-01 00:02:10.000 | 2009-01-01 00:05:24.000 |
| 2009-01-01 00:02:19.000 | 2009-01-01 00:02:35.000 |
| 2009-01-01 00:02:57.000 | 2009-01-01 00:04:04.000 |
| 2009-01-01 00:04:12.000 | 2009-01-01 00:04:52.000 |
+-------------------------+-------------------------+
Run Code Online (Sandbox Code Playgroud)

这是一种用查询实现相同想法的方法,为每个调用开始添加1,为每个结尾减1.

  SELECT starttime AS ts, +1 AS TYPE,
    ROW_NUMBER() OVER(ORDER BY starttime) AS start_ordinal
  FROM Calls
Run Code Online (Sandbox Code Playgroud)

C1 CTE的这一部分将记录每个呼叫的每个启动时间并对其进行编号

+-------------------------+------+---------------+
|           ts            | TYPE | start_ordinal |
+-------------------------+------+---------------+
| 2009-01-01 00:02:10.000 |    1 |             1 |
| 2009-01-01 00:02:19.000 |    1 |             2 |
| 2009-01-01 00:02:57.000 |    1 |             3 |
| 2009-01-01 00:04:12.000 |    1 |             4 |
+-------------------------+------+---------------+
Run Code Online (Sandbox Code Playgroud)

现在这段代码

  SELECT endtime, -1, NULL
  FROM Calls
Run Code Online (Sandbox Code Playgroud)

将生成所有"endtimes"而不进行行编号

+-------------------------+----+------+
|         endtime         |    |      |
+-------------------------+----+------+
| 2009-01-01 00:02:35.000 | -1 | NULL |
| 2009-01-01 00:04:04.000 | -1 | NULL |
| 2009-01-01 00:04:52.000 | -1 | NULL |
| 2009-01-01 00:05:24.000 | -1 | NULL |
+-------------------------+----+------+
Run Code Online (Sandbox Code Playgroud)

现在让UNION拥有完整的C1 CTE定义,你将两个表混合在一起

+-------------------------+------+---------------+
|           ts            | TYPE | start_ordinal |
+-------------------------+------+---------------+
| 2009-01-01 00:02:10.000 |    1 |             1 |
| 2009-01-01 00:02:19.000 |    1 |             2 |
| 2009-01-01 00:02:57.000 |    1 |             3 |
| 2009-01-01 00:04:12.000 |    1 |             4 |
| 2009-01-01 00:02:35.000 | -1   |     NULL      |
| 2009-01-01 00:04:04.000 | -1   |     NULL      |
| 2009-01-01 00:04:52.000 | -1   |     NULL      |
| 2009-01-01 00:05:24.000 | -1   |     NULL      |
+-------------------------+------+---------------+
Run Code Online (Sandbox Code Playgroud)

计算C2并使用新列对C1进行排序和编号

C2 AS
(
  SELECT *,
    ROW_NUMBER() OVER(  ORDER BY ts, TYPE) AS start_or_end_ordinal
  FROM C1
)

+-------------------------+------+-------+--------------+
|           ts            | TYPE | start | start_or_end |
+-------------------------+------+-------+--------------+
| 2009-01-01 00:02:10.000 |    1 | 1     |            1 |
| 2009-01-01 00:02:19.000 |    1 | 2     |            2 |
| 2009-01-01 00:02:35.000 |   -1 | NULL  |            3 |
| 2009-01-01 00:02:57.000 |    1 | 3     |            4 |
| 2009-01-01 00:04:04.000 |   -1 | NULL  |            5 |
| 2009-01-01 00:04:12.000 |    1 | 4     |            6 |
| 2009-01-01 00:04:52.000 |   -1 | NULL  |            7 |
| 2009-01-01 00:05:24.000 |   -1 | NULL  |            8 |
+-------------------------+------+-------+--------------+
Run Code Online (Sandbox Code Playgroud)

并且有魔力发生的地方,#start - #ends的结果随时都是当前并发呼叫的数量.

对于每个Type = 1(开始事件),我们在第3列中有#start值.我们还有#start + #end(在第4列)

#start_or_end = #start + #end

#end = (#start_or_end - #start)

#start - #end = #start - (#start_or_end - #start)

#start - #end = 2 * #start - #start_or_end
Run Code Online (Sandbox Code Playgroud)

所以在SQL中:

SELECT MAX(2 * start_ordinal - start_or_end_ordinal) AS mx
FROM C2
WHERE TYPE = 1
Run Code Online (Sandbox Code Playgroud)

在这种情况下,使用前置调用集,结果为2.

在所提出的文章中,通过例如服务或"电话公司"或"电话中心"来获得分组结果有一点改进,并且该想法也可以用于例如按时隙分组并具有最大并发性在给定的一天中每小时一小时.


Pis*_*3.0 1

SELECT COUNT(*) FROM calls 
    WHERE '2010-06-15 15:00:00' BETWEEN calls.starttime AND calls.endtime
Run Code Online (Sandbox Code Playgroud)

并每秒重复此操作。