计算表格中显示的时间间隔联合的总长度

时间:2014-04-28 09:26:04

标签: mysql datetime intervals

我希望计算表格中显示的时间间隔联合的总时间长度。

例如,给出以下内容:

mysql> SELECT * FROM Temp;
+------+---------------------+---------------------+
| id   | start               | end                 |
+------+---------------------+---------------------+
|    1 | 2010-01-01 10:00:00 | 2010-01-01 11:00:00 |
|    2 | 2010-01-01 12:00:00 | 2010-01-01 14:00:00 |
|    3 | 2010-01-01 13:00:00 | 2010-01-01 15:00:00 |
+------+---------------------+---------------------+

我想以某种方式选择总时间长度,在这种情况下是4小时(间隔(10:00,11:00)和(12:00,15:00)联合的总时间长度)。

我不在乎输出是以秒为单位(INT还是FLOAT),还是其他任何合理的格式。

值得一提的是,我不确定表中日期的顺序。无法保证以任何方式对开始日期时间或结束日期时间进行排序。我也不能说一个“典型的”日期时间间隔 - 例如,它可能超过一天。

可以说,任何单个时间间隔都是非负长度。也就是说,对于任何记录,结束日期至少与开始日期一样晚。


我知道如何用简单的编程语言(如Python)完成这项任务;我只是想知道在纯MySQL中是否有一种明智的方法可以做到这一点。如果没有,我会选择所有内容并用其他编程语言处理它。因此,“如果没有一些非常认真的努力,在MySQL中完成这一点是不可能的”也可以作为这个问题的合法答案......


我见过this question这是类似的,但约为tsql。在那里提出的解决方案是使用MySQL不知道的语法,例如cross apply,我翻译的尝试失败了。


根据要求,以下是创建示例数据的查询:

CREATE TABLE Temp (id INT, start DATETIME, end DATETIME);
INSERT INTO Temp (id, start, end) VALUES (1, '2010-01-01 10:00', '2010-01-01 11:00');
INSERT INTO Temp (id, start, end) VALUES (2, '2010-01-01 13:00', '2010-01-01 14:00');
INSERT INTO Temp (id, start, end) VALUES (3, '2010-01-01 11:00', '2010-01-01 16:00');

所以数据如下:

+------+---------------------+---------------------+
| id   | start               | end                 |
+------+---------------------+---------------------+
|    1 | 2010-01-01 10:00:00 | 2010-01-01 11:00:00 |
|    2 | 2010-01-01 13:00:00 | 2010-01-01 14:00:00 |
|    3 | 2010-01-01 11:00:00 | 2010-01-01 16:00:00 |
+------+---------------------+---------------------+

此示例数据的结果应为6小时。

1 个答案:

答案 0 :(得分:1)

<强>声明

这可能最好在SQL之外完成

对于那些喜欢痛苦查询的人

您可以创建一个查询,尝试确定表中其他位置是否与end列重叠的行。如果没有,请尝试找出end列与最近的startgap之间的时间。

然后从整个表格中取出最大end,从整个表格中减去最小start,最后减去gap列的总数:

select
unix_timestamp(maxEnd)-unix_timestamp(minSt)-sum(case when hasEndOverlap=0 then gap else 0 end) as unionSecs,
(unix_timestamp(maxEnd)-unix_timestamp(minSt)-sum(case when hasEndOverlap=0 then gap else 0 end))/(60*60) as unionHrs
from
(
 select c.id,c.`start`,c.`end`,
 c.minSt,c.maxEnd,
 c.hasEndOverlap,
 @prevSt,
 unix_timestamp(@prevSt)-unix_timestamp(c.`end`) as gap,
 @prevSt := c.`start`
 from
 (
  select t.id,t.`start`,t.`end`,
  a.minSt,a.maxEnd,
  case when min(te.id) is null and t.`end` != a.maxEnd then 0 else 1 end as hasEndOverlap
  from Temp t
  left outer join Temp te on t.`end` >= te.`start` and t.`end` <= te.`end` and t.id != te.id
  join (select min(`start`) as minSt,max(`end`) as maxEnd from test.`Temp`) a
  group by t.id,t.`start`,t.`end`
  ) c
  join (select @prevSt := '1970-01-01') r
  order by c.`end` desc
) d
group by minSt,maxEnd
;