2009-07-18 102 views
4

我有一个事件表,每个表中有一个MySQL表中的StartTime和EndTime(类型为DateTime)。MySQL中重叠日期时间范围的总和

我在输出重叠时间总和和重叠事件的数量。

在MySQL中执行此查询的最有效/最简单的方法是什么?

CREATE TABLE IF NOT EXISTS `events` (
    `EventID` int(10) unsigned NOT NULL auto_increment, 
    `StartTime` datetime NOT NULL, 
    `EndTime` datetime default NULL, 
    PRIMARY KEY (`EventID`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=37 ; 


INSERT INTO `events` (`EventID`, `StartTime`, `EndTime`) VALUES 
(10001, '2009-02-09 03:00:00', '2009-02-09 10:00:00'), 
(10002, '2009-02-09 05:00:00', '2009-02-09 09:00:00'), 
(10003, '2009-02-09 07:00:00', '2009-02-09 09:00:00'); 


# if the query was run using the data above, 
# the table below would be the desired output 

# Number of Overlapped Events | Total Amount of Time those events overlapped. 
1, 03:00:00 
2, 02:00:00 
3, 02:00:00 

这些结果的目的是生成使用小时的帐单。 (如果您有一个活动正在运行,您可能每小时支付10美元,但如果有两个活动正在运行,则只需每小时支付8美元,但仅限于您有两个活动正在运行的时间段。)

+0

这个问题缺乏清晰度。 这样一个问题的用途是什么,你的建议有缺陷。 3小时 - 只有一个事件正在运行(3a到5a和9a到10a) 2小时 - 两个事件是(并发)运行2小时 - 所有三个事件都在运行(7a到9a) 7到9之间有3个并发事件,所以你的中间子弹是错误的,没有任何问题。 – Eddie 2009-07-20 17:42:08

+0

我对这种困惑表示歉意,我编辑了这个问题以提高清晰度,并在问题背后增加了目的。 当你说“提案有缺陷”时,我不明白你的意思。你是绝对正确的,在7到9之间有3个并发事件,但是我的问题在上面的例子中已经提到过了(这是第3行)。 您提到的行(第2行)是5a到7a期间,而不是7a到9a。 我希望包含的SQL澄清这一点。 – maxsilver 2009-07-20 23:12:50

回答

4

试试这个:

SELECT `COUNT`, SEC_TO_TIME(SUM(Duration)) 
FROM (
    SELECT 
     COUNT(*) AS `Count`, 
     UNIX_TIMESTAMP(Times2.Time) - UNIX_TIMESTAMP(Times1.Time) AS Duration 
    FROM (
     SELECT @rownum1 := @rownum1 + 1 AS rownum, `Time` 
     FROM (
      SELECT DISTINCT(StartTime) AS `Time` FROM events 
      UNION 
      SELECT DISTINCT(EndTime) AS `Time` FROM events 
     ) AS AllTimes, (SELECT @rownum1 := 0) AS Rownum 
     ORDER BY `Time` DESC 
    ) As Times1 
    JOIN (
     SELECT @rownum2 := @rownum2 + 1 AS rownum, `Time` 
     FROM (
      SELECT DISTINCT(StartTime) AS `Time` FROM events 
      UNION 
      SELECT DISTINCT(EndTime) AS `Time` FROM events 
     ) AS AllTimes, (SELECT @rownum2 := 0) AS Rownum 
     ORDER BY `Time` DESC 
    ) As Times2 
    ON Times1.rownum = Times2.rownum + 1 
    JOIN events ON Times1.Time >= events.StartTime AND Times2.Time <= events.EndTime 
    GROUP BY Times1.rownum 
) Totals 
GROUP BY `Count` 

结果:

1, 03:00:00 
2, 02:00:00 
3, 02:00:00 

如果你想这是什么没有做,或者你想一些解释,请让我知道。通过将重复的子查询AllTimes存储在临时表中,可以使其更快,但希望其运行速度足够快。

0

以包含单个日期时间字段作为其主键的表格开始,并用您感兴趣的每个时间值填充该表格。闰年有527040分钟(31622400秒),因此如果您的事件发生此表格可能会变大跨越数年。

现在加入对这个表做这样

SELECT i.dt as instant, count(*) as events 
FROM instant i JOIN event e ON i.dt BETWEEN e.start AND e.end 
GROUP BY i.dt 
WHERE i.dt BETWEEN ? AND ? 

有东西在instant.dt索引可以让你放弃了一个ORDER BY。

如果不经常添加事件,则可能需要通过脱机运行查询来预先计算这些内容,然后填充单独的表。

-1

我会建议一个具有开始时间,结束时间,#个事件的内存中结构...(这会简化为时间(小时),但使用unix时间会导致第二个精度)

对于每个事件,如果没有重叠,您将按原样插入新事件,否则,找到重叠部分,并将事件分割为(最多3个)可能重叠的部分,使用示例数据,从第一个事件:

事件1点开始在凌晨3点和上午10时结束:只需添加事件,因为没有重叠:

3,10,1 

事件2点开始在上午05时和上午9点多结束:重叠,所以分裂原,并用额外的“#events”

3,5,1 
    5,9,2 
    9,10,1 

事件3点开始在早上7点添加新的,并在上午9时结束:也重叠,做相同的各个时期:

3,5,1 
    5,7,2 
    7,9,3 
    9,10,1 

所以计算每#events的重叠时间:

1 event= (5-3)+(10-9)=3 hours 
2 events = 7-5 = 2 hours 
3 events = 9-7 = 2 hours 

这将是有意义的运行此作为后台进程,如果有很多事件要比较。