我有一个简单的事件表:
event_id | start_time | end_time
如何查询最大同时事件数?
答案 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
这种快速方法有一点点缺点......如果您的事件通常跨越一个多小时,那么在下一个小时结束的事件可能仍会重叠,但不会被计算在内。要解决此问题,只需按较大的间隔进行分组,例如一天或一周。有点毛茸茸,但效果很好,很快就会给你听起来像你正在寻找的结果。