我遇到了以下问题:
我想总结每个名字的小时数,给出START
和END
个活动之间的总时间间隔,
如果我可以从每个记录中减去开头的结尾会更简单,例如,玛丽,从第13开始并且在15和16开始另一个活动而在14和16,我希望它的结果是3(她使用3他们有时间进行这两项活动)
e.g:
Name | START | END |
-----------------------------------------------------------
KATE | 2014-01-01 13:00:00 | 2014-01-01 14:00:00 |
MARY | 2014-01-01 13:00:00 | 2014-01-01 15:00:00 |
TOM | 2014-01-01 13:00:00 | 2014-01-01 16:00:00 |
KATE | 2014-01-01 12:00:00 | 2014-01-02 04:00:00 |
MARY | 2014-01-01 14:00:00 | 2014-01-01 16:00:00 |
TOM | 2014-01-01 12:00:00 | 2014-01-01 18:00:00 |
TOM | 2014-01-01 22:00:00 | 2014-01-02 02:00:00 |
结果:
KATE 15 hours
MARY 3 hours
TOM 9 hours
答案 0 :(得分:1)
您是否尝试了一个分组,然后是一个聚合函数?
SELECT Name, SUM(UNIX_TIMESTAMP(End) - UNIX_TIMESTAMP(Start)) FROM myTable
GROUP BY Name
这将从您拥有的时间间隔返回累计总秒数。然后,您可以将秒数更改为小时以进行显示。
此外,我强烈建议使用主键或其他内容进行分组,而不是字符串名称,但我知道这可能只是为了简化问题。
答案 1 :(得分:0)
我发现这个问题很有趣,所以花了一点时间来开发解决方案。我想出的是按名称和开始时间对行进行排序,然后使用MySQL变量来计算重叠范围。我首先对表格进行排序,并使用带有名称和时间从一行到下一行的列进行补充
SELECT [expounded below]
FROM (SELECT * FROM tbl ORDER BY Name, START, END) AS u,
(SELECT @x := 0, @gap := 0, @same_name:='',
@beg := (SELECT MIN(START) FROM tbl),
@end := (SELECT MAX(END) FROM tbl)) AS t
这会将时间范围的名称和外部边界添加到表的每一行,以及对表进行排序,以便 名称按开始时间顺序排列在一起。对于每一行,我们现在将@same_name,@ beg和@end的值从一行转发到下一行,而@x和@gap将累积小时数。
现在我们必须对可能发生的重叠做一些推理。对于任何两个区间,它们要么是不相交的,要么是交叉的:
Non-overlapping: beg--------end START-------END
Overlapping: beg-----------end beg---------end
START--------------END START-----------END
Subset: beg---------------------------------end
START-----END
一旦行相邻,我们可以通过比较它们的起点和终点来决定两个范围是否重叠。它们重叠 如果一个的开头在另一个的结束之前,反之亦然:
IF( @end >= START && @beg <= END,
如果它们重叠,则总间隔是两个间隔的外边缘之间的差异:
TIMESTAMPDIFF(HOUR, LEAST(@beg, START), GREATEST(@end, END))
如果它们不重叠,那么我们可以将新间隔添加到前一个间隔。
我们还需要知道间隔之间的差距,这是从第一个结束到第二个结尾的差异。这对于计算超过两个区间的情况的小时数是必要的,其中只有一些区间重叠。
1-----------2 3----------4
3--------------------5
将这些放在一起让我们得到每行的计算,其中每一行计算小时与一个的联合 它上面。对于每个变量,如果名称发生变化,我们必须重置它:
SELECT Name, START, END,
@x := IF(@same_name = Name,
IF( @end >= START && @beg <= END, -- does it overlap?
TIMESTAMPDIFF(HOUR, LEAST(@beg, START), GREATEST(@end, END)),
@x + TIMESTAMPDIFF(HOUR, START, END) ),
TIMESTAMPDIFF(HOUR,START,END) ) AS hr,
@gap := IF(@same_name = Name,
IF(@end >= START && @beg <= END, -- does it overlap?
@gap,
@gap + TIMESTAMPDIFF(HOUR, @end, START)),
0) AS gap,
@beg := IF(@same_name = Name,
CAST(LEAST(@beg, START) AS DATETIME), -- expand interval
START) AS beg, -- reset interval
@end := IF(@same_name = Name,
CAST(GREATEST(@end, END) AS DATETIME),
END) AS finish,
@same_name := Name AS sameName
FROM
(SELECT * FROM xt ORDER BY Name, START, END) AS u,
(SELECT @x := 0, @gap := 0, @same_name:='', @beg := (SELECT MIN(START) FROM xt), @end := (SELECT MAX(END) FROM xt)) AS t
这仍然给我们提供了与原始表中一样多的行。每个名称都会累积小时和差距,因此我们必须选择最高值并按名称分组:
SELECT Name, MAX(hr) - MAX(gap) AS HOURS
FROM ( [insert above query here] ) AS intermediateCalculcation
GROUP BY Name;
修改强> 当然,在进入后一刻,我发现(a)有一个没有重叠间隔的名字的错误;并且(b)所有@x实际上正在为eacdh名称建立从MIN(START)到MAX(END)的间隔,这可以通过更简单的查询和连接来完成。嗯,为读者锻炼? : - )