查询1天内两次的持续时间

时间:2011-03-14 02:46:07

标签: mysql

假设我有一个包含有关流媒体连接信息的表。在此表中,我有一个开始时间和结束时间,用于启动连接,然后关闭。

表:日志

  • id(INT,PK,AUTO_INCREMENT)
  • StartTime(DATETIME)
  • EndTime(DATETIME)

我希望能够运行一个查询,该查询将累计建立一天的连接总时间。这对于一天内的连接来说是显而易见的:

SELECT
    SUM(
        TIME_TO_SEC(
            TIMEDIFF(`EndTime`, `StartTime`)
        )
    )
WHERE (`StartTime` BETWEEN '2010-01-01' AND '2010-01-02);

但是,假设一天StartTime开始,比如说晚上11点左右,EndTime是第二天的某个时间,也许是凌晨3点。在这些情况下,我只想分配当天发生的那段时间。因此,第一天将持续1小时,下一阶段将持续3小时。

SUM(
    TIME_TO_SEC(
        TIMEDIFF(
            IF(`EndTime`>DATE_ADD('2010-01-01', INTERVAL 1 DAY), DATE_ADD('2010-01-01', INTERVAL 1 DAY), `EndTime`), 
            IF(`StartTime`<'2010-01-01', '2010-01-01', `StartTime`)
        )
    )/60/60
)

这样做的想法是,如果EndTime超过一天结束,那么我们只会使用当天结束。如果StartTime小于当天的开头,那么我们只会使用当天的开头。

所以,我需要把这一切都包装成一个会产生如下表格的东西:

date, total
2010-01-01, 0
2010-01-02, 1.53
2010-01-03, 5.33

我认为这个查询可行:

SELECT 
`date`,
SUM(
    TIME_TO_SEC(
        TIMEDIFF(
            IF(`EndTime`>DATE_ADD(`date`, INTERVAL 1 DAY), DATE_ADD(`date`, INTERVAL 1 DAY), `EndTime`), 
            IF(`StartTime`<`date`, `date`, `StartTime`)
        )
    )/60/60
) AS `total_hours`
FROM 
(SELECT * FROM `logs` WHERE `StartTime` BETWEEN '2010-08-01' AND '2010-08-31') AS logs_small,
(SELECT DATE_ADD("2010-08-01", INTERVAL `number` DAY) AS `date` FROM `numbers` WHERE `number` BETWEEN 0 AND 30) AS `dates`
GROUP BY `date`;

请注意,引用的numbers表是一个只有一列number的表,带有一系列整数,0,1,2,3等。我在这里用它来生成一个系列日期,工作正常。

此查询的问题是我收到的数据不准确。具体来说,logs表中具有进入第二天的EndDate的行不会在第二天计算任何时间。例如,如果我有一行开始于2010-08-01 23:00:00并结束2010-08-02 01:00:00,那么2010-08-02的结果行将加起来为0. / p>

有更好的方法吗?理想情况下,我希望在没有任何与其匹配的记录的日子里获取0而不是null

编辑:为了澄清,我想转此:

id, StartTime, EndTime
0, 2000-01-01 01:00:00, 2000-01-01 04:00:00
1, 2000-01-01 23:00:00, 2000-01-02 05:00:00
2, 2000-01-02 00:00:00, 2000-01-04 01:00:00

......进入这个:

date, total_hours
2000-01-01, 4
2000-01-02, 29
2000-01-03, 24
2000-01-04, 1
2000-01-05, 0

解决方案

感谢jim31415推出解决方案!我将他的答案翻译成了MySQL中可用的函数,并提出了这个:

SELECT `d`.`Date`,
        SUM(COALESCE( 
        (CASE WHEN t.StartTime >= d.Date AND t.EndTime < DATE_ADD(d.Date, INTERVAL 1 DAY) THEN TIME_TO_SEC(TIMEDIFF(t.EndTime, t.StartTime))
              WHEN t.StartTime < d.Date AND t.EndTime <= DATE_ADD(d.Date, INTERVAL 1 DAY) THEN TIME_TO_SEC(TIMEDIFF(t.EndTime,d.Date))
              WHEN t.StartTime >= d.Date AND t.EndTime > DATE_ADD(d.Date, INTERVAL 1 DAY) THEN TIME_TO_SEC(TIMEDIFF(DATE_ADD(d.Date, INTERVAL 1 DAY),t.StartTime))
              WHEN t.StartTime < d.Date AND t.EndTime > DATE_ADD(d.Date, INTERVAL 1 DAY) THEN 24*60*60

         END), 0)
         )/60/60 ConnectionTime
FROM (SELECT DATE_ADD('2011-03-01', INTERVAL `number` DAY) AS `Date` FROM `numbers` WHERE `number` BETWEEN 0 AND 30) AS d
LEFT JOIN `logs` t ON (t.StartTime >= d.Date AND t.StartTime < DATE_ADD(d.Date, INTERVAL 1 DAY))
                        OR (t.EndTime >= d.Date AND t.EndTime < DATE_ADD(d.Date, INTERVAL 1 DAY))
                        OR (t.StartTime < d.Date AND t.EndTime > DATE_ADD(d.Date, INTERVAL 1 DAY))
GROUP BY d.Date
ORDER BY d.Date;

我还应该注意EndTime的空值不适用于我的情况,因为我正在读取应用程序中的旧日志文件。如果你需要它们,吉姆的帖子很好地概括了它们。

3 个答案:

答案 0 :(得分:2)

这是在MS SQL中,但我认为逻辑适用并且可以转换为MySQL。 我不确定你想如何处理null的EndTime,所以我评论了这一点。

select d.Date,
        sum(coalesce( 
        (case when t.StartTime >= d.Date and t.EndTime < dateadd(day,1,d.Date) then datediff(minute,t.StartTime,t.EndTime)
              when t.StartTime < d.Date and t.EndTime <= dateadd(day,1,d.Date) then datediff(minute,d.Date,t.EndTime)
              when t.StartTime >= d.Date and t.EndTime > dateadd(day,1,d.Date) then datediff(minute,t.StartTime,dateadd(day,1,d.Date))
              when t.StartTime < d.Date and t.EndTime > dateadd(day,1,d.Date) then 24*60
                --when t.StartTime >= d.Date and t.EndTime is null then datediff(minute,t.StartTime,getdate())
                --when t.StartTime < d.Date and t.EndTime is null then datediff(minute,d.Date,getdate())
         end), 0)
         ) ConnectionTime
from (select Date=dateadd(day, num, '2011-03-01') from #NUMBERS where num between 0 and 30) d
left join Logs t on (t.StartTime >= d.Date and t.StartTime < dateadd(day,1,d.Date))
                        or (t.EndTime >= d.Date and t.EndTime < dateadd(day,1,d.Date))
                        or (t.StartTime < d.Date and t.EndTime > dateadd(day,1,d.Date))
group by d.Date
order by d.Date

答案 1 :(得分:1)

使用联合使自己更容易

SELECT 
`date`,
SUM(
    TIME_TO_SEC(TIMEDIFF(`EndTime`,`StartTime`))/60/60
) AS `total_hours`
FROM 

(SELECT id, starttime, if (endtime > date then date else endtime) FROM `logs` WHERE `StartTime` >= date AND `StartTime` < date
union all
SELECT id, date, endtime FROM `logs` WHERE `enddate` >= date AND `enddate` < date and !(`StartTime` >= date AND `StartTime` < date)
union all
SELECT id, date, date_add(date, 1) FROM `logs` WHERE `enddate` > date AND `startdate` < date
) as datedetails inner join
  (SELECT DATE_ADD("2010-08-01", INTERVAL `number` DAY) AS `date` FROM `numbers` WHERE `number` BETWEEN 0 AND 30) AS `dates`
GROUP BY `date`;

希望,我理解你的问题

编辑:当有多天请求在要求的日期之前开始并在

之后结束时忘记了案例

答案 2 :(得分:1)

使用此

select startTime,duration  as duration,time,TIME_TO_SEC(TIMEDIFF(time,startTime))  as diff from <idling> limit 25;

select startTime,duration DIV 60  as duration,time,TIMESTAMPDIFF(MINUTE,startTime,time)  as diff from <idling> limit 25;