查询最大并发事件数

时间:2009-01-17 00:06:28

标签: sql mysql postgresql

我有一个简单的事件表:

event_id | start_time | end_time

如何查询最大同时事件数?

4 个答案:

答案 0 :(得分:4)

我的回答与哈利的第一个回答非常相似。我会尝试进行略微不同的性能优化...跳到最后,以避免对为什么......的漫无边际的解释...

哈利的第一个答案(核心逻辑)

SELECT MAX(overlapAtEnd)
FROM
(
    SELECT 
        COUNT(1) AS overlapAtEnd                        
    FROM 
        your_table AS t1, 
        your_table AS t2
    WHERE 
        t1.end_time BETWEEN t2.start_time AND t2.end_time
    GROUP BY t1.event_id
) AS foo

占用处理时间最长的地方是加入。

对于表格中的每条记录,您选择(t1.end time)。然后再次搜索表(t1.end_time> = start_time)以及您搜索的所有匹配记录(t1.end_time< = t1.end_time)

现在,您可以很容易地在start_time上创建索引。这使得第一次检查(t1.end_time> = start_time)快得多;
- 索引是用于极快搜索的搜索树 - 这使得查找第一个匹配记录的速度非常快 - 指数基本上是订购的 - 这意味着它知道“第一场比赛后的所有内容也匹配”

最后一部分虽然是关键,因为它意味着......即使使用索引进行第一次检查(t1.end_time> = start_time),我们仍然可以留下大量记录来制作第二部分check(t1.end_time< = t1.end_time)

[包括索引中的end_time在这里没有帮助,很快就会讨论]

0, '10:00', '10:04'   COUNT(*) WHERE '10:04' >= start_time  ==  4
1, '10:01', '10:06'   COUNT(*) WHERE '10:06' >= start_time  ==  4
2, '10:02', '10:09'   COUNT(*) WHERE '10:09' >= start_time  ==  5
3, '10:04', '10:07'   COUNT(*) WHERE '10:07' >= start_time  ==  4
4, '10:08', '10:12'   COUNT(*) WHERE '10:12' >= start_time  ==  6
5, '10:12', '10:17'   COUNT(*) WHERE '10:17' >= start_time  ==  7
6, '10:15', '10:18'   COUNT(*) WHERE '10:18' >= start_time  ==  8
7, '10:18', '10:22'   COUNT(*) WHERE '10:22' >= start_time  ==  10
8, '10:19', '10:24'   COUNT(*) WHERE '10:24' >= start_time  ==  10
9, '10:22', '10:25'   COUNT(*) WHERE '10:25' >= start_time  ==  10

=> leaves 68 rows to check the second condition; (t1.end_time <= t1.end_time)

假设事件的分布相对平稳,每条记录(大约和平均)将与表的一半匹配。这意味着您正在进行(n * n / 2)检查,其中n是表中的记录数。即使在100条记录中,也可以进行5000次检查在2000年的记录中,你正在做大约200万次检查!

自然倾向于将end_time字段添加到索引中。然而,这没有帮助。 (start_time,end_time)的索引创建一个搜索树,直到每个唯一的start_time,然后在每个唯一的start_time下面有一个单独的end_times搜索树。

在上面的示例中,每个start_time都是唯一的。这意味着您仍需要执行所有68次end_time检查。只有start_time检查才能从索引中受益。

我们需要做的是尝试使用单个“start_time”索引来做比现在更多的事情。我们需要为查询引擎提供更多信息。

一个例子是使用“最大事件持续时间”。例如,我们可能会发现任何事件都不会超过8分钟。这将给我们以下查询...

SELECT MAX(overlapAtEnd)
FROM
(
    SELECT 
        COUNT(1) AS overlapAtEnd                        
    FROM 
        your_table AS t1, 
        your_table AS t2
    WHERE 
            t1.end_time >= t2.start_time
        AND t1.end_time <= t2.end_time
        AND t1.end_time <= t2.start_time + [max_event_duration] 
    GROUP BY t1.event_id
) AS foo

在上面给出的示例中应用8分钟持续时间的示例,我们将68次end_time检查减少到34次。

0, '10:00', '10:04'   COUNT(*) WHERE '10:04' BETWEEN start_time AND start_time + 8 == 4
1, '10:01', '10:06'   COUNT(*) WHERE '10:06' BETWEEN start_time AND start_time + 8 == 4
2, '10:02', '10:09'   COUNT(*) WHERE '10:09' BETWEEN start_time AND start_time + 8 == 4
3, '10:04', '10:07'   COUNT(*) WHERE '10:07' BETWEEN start_time AND start_time + 8 == 4
4, '10:08', '10:12'   COUNT(*) WHERE '10:12' BETWEEN start_time AND start_time + 8 == 3
5, '10:12', '10:17'   COUNT(*) WHERE '10:17' BETWEEN start_time AND start_time + 8 == 2
6, '10:15', '10:18'   COUNT(*) WHERE '10:18' BETWEEN start_time AND start_time + 8 == 3
7, '10:18', '10:22'   COUNT(*) WHERE '10:22' BETWEEN start_time AND start_time + 8 == 4
8, '10:19', '10:24'   COUNT(*) WHERE '10:24' BETWEEN start_time AND start_time + 8 == 3
9, '10:22', '10:25'   COUNT(*) WHERE '10:25' BETWEEN start_time AND start_time + 8 == 3

=> leaves 34 rows to check the second condition; (t1.end_time <= t1.end_time)
=> thats half the original 68, and on bigger tables the benefit increases...

即使我们不知道事件永远不会超过8分钟,我们也可以通过查看10条记录来找到它。超过10条记录的MAX(end_time - start_time)仍然比通过34种记录组合检查(t1.end_time&lt; = t1.end_time)更快。

随着桌子的大小增加,效益也会增加。实际上,在[max_event_duration]明显小于表所涵盖的整个时间跨度的情况下,您将(n n / 2)平方定律更改为更像(n x + n)的内容。是线性的。

民主党。

SELECT
   MAX(overlapAtEnd)
FROM
(
    SELECT 
        COUNT(1) AS overlapAtEnd                        
    FROM 
        your_table AS t1, 
        your_table AS t2
    WHERE 
            t2.start_time <= t1.end_time
        AND t2.start_time >= t1.end_time - (SELECT MAX(end_time - start_time) FROM your_table)
        AND t2.end_time   >= t1.end_time
    GROUP BY t1.event_id
) AS foo

答案 1 :(得分:2)

如同其他答案所述,根据您的同时意思,这可能与this question非常相似。

不幸的是,我提出的解决方案(这是接受的答案)会要求您重新设计表格。但是,通过检查“SessionCount”(或类似命名的)列,它可以让您轻松确定最大同时事件数。

答案 2 :(得分:0)

我会在很多次传递中执行此操作,这是一个非常慢的解决方案,但可能没有一种非常快速的方法来执行此操作。并且基于Daniel Paull的答案的解决方案会快得多。

按开始时间对事件进行排序。 循环遍历事件并找到没有事件的间隙,在这些间隙之间分组事件。 每次在每个组内循环(以您的时间记录的任何分辨率)并查询当时正在进行的事件。根据编程语言的速度与数据库查询的速度,您可以查看重叠事件并跳转到其中一个重叠事件的第一个end_time。

答案 3 :(得分:0)

由于你的高峰时间总是在end_time结束,你可以检查那些像Sparr建议的那样。因此,请执行两次连接相同表的查询,并计算事件在每个end_time重叠的行数。然后取最大值。

这会慢慢给你答案:

SELECT MAX(overlapAtEnd)
FROM
(
    SELECT 
        COUNT(1) AS overlapAtEnd            
    FROM 
        your_table AS t1, 
        your_table AS t2
    WHERE 
        t1.end_time BETWEEN t2.start_time AND t2.end_time
    GROUP BY t1.event_id
) AS foo

将它分解成更小的组(少与之比较),然后获得这些较小组的最大值可以显着提高它:

SELECT MAX(maxOLP)
FROM
(
    SELECT MAX(olp) AS maxOLP
    FROM
    (
        SELECT 
            MAX(overlapAtEnd) AS maxOLP,
            EXTRACT(HOUR FROM t1.end_time)  AS hr
        FROM
        (
            SELECT 
                COUNT(1) AS overlapAtEnd            
            FROM 
                your_table AS t1, 
                your_table AS t2
            WHERE 
                t1.end_time BETWEEN t2.start_time AND t2.end_time
            GROUP BY t1.event_id
        ) AS foo
        GROUP BY t1.event_id, EXTRACT(HOUR FROM t1.end_time)
    ) AS foo
    GROUP BY hr
) AS foo2

这种快速方法有一点点缺点......如果您的事件通常跨越一个多小时,那么在下一个小时结束的事件可能仍会重叠,但不会被计算在内。要解决此问题,只需按较大的间隔进行分组,例如一天或一周。有点毛茸茸,但效果很好,很快就会给你听起来像你正在寻找的结果。