假设我有一个包含有关流媒体连接信息的表。在此表中,我有一个开始时间和结束时间,用于启动连接,然后关闭。
表:日志
我希望能够运行一个查询,该查询将累计建立一天的连接总时间。这对于一天内的连接来说是显而易见的:
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
的空值不适用于我的情况,因为我正在读取应用程序中的旧日志文件。如果你需要它们,吉姆的帖子很好地概括了它们。
答案 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;