MYSQL - 总和间隔日期

时间:2014-10-22 01:21:16

标签: mysql datetime

我遇到了以下问题:

我想总结每个名字的小时数,给出STARTEND个活动之间的总时间间隔, 如果我可以从每个记录中减去开头的结尾会更简单,例如,玛丽,从第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

2 个答案:

答案 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)的间隔,这可以通过更简单的查询和连接来完成。嗯,为读者锻炼? : - )