我想计算一天中2个日期与mysql中的多行之间的时间

时间:2017-06-10 17:20:12

标签: mysql sql

我正在使用此查询来计算用户在应用中的全天和前5天的登录时间

Select 
  sec_to_time(sum(time_to_sec(TIMEDIFF((IFNULL(logoff_time, ADDTIME(now(), '05:00:00'))),login_time)))) as online_time 
from tb_sessions  
WHERE 
  (login_time BETWEEN DATE(DATE_ADD(now(), INTERVAL (-6) DAY)) 
AND
  ADDTIME(now(), '5:00:00')) AND user_id = 30982
AND TIME(`login_time`) between "00:00:00" AND "23:59:59"
group by DATE(login_time)

现在我有了一些新的要求:

计算从07:00:00到23:59:59的时间

我的表: tb_sessions

id |   user_id   |       login_time      |   logoff_time

1        3098       2017-06-10 06:30:00    2017-06-10 07:45:00
2        3098       2017-06-10 07:45:01    2017-06-10 08:30:00

通过使用上述查询,总的橄榄色时间是= 02:00:00 但我只想要时间从7:00到8:30,所以总时间将是= 1:30:00 我对查询进行了一些更改,但没有成功。

您可以在以下链接中查看我的查询:

  

http://sqlfiddle.com/#!9/4620af/12

1 个答案:

答案 0 :(得分:1)

您可以使用greatest在同一天使用最新日期login_time和7:00,然后再次使用greatest排除负时差(同时注销时)时间是在7:00之前):

Select   date(login_time) date,
         time_format(sec_to_time(sum(greatest(0, time_to_sec(timediff(
             ifnull(logoff_time, now()), 
             greatest(login_time, date_add(date(login_time), interval 7 hour))
         ))))), '%H:%i:%s') online
from     tb_sessions  
where    login_time between date(date_add(now(), interval (-3) day)) and now() 
and      user_id = 3098
and      time(login_time) between "00:00:00" and "23:59:59"
group by date(login_time)

sqlfiddle

上查看它

解释

内部greatest调用如下所示:

greatest(login_time, date_add(date(login_time), interval 7 hour))

第二个参数仅取自login_time的日期,因此它对应于当天的午夜,然后为其添加7个小时:因此这表示当天的7:00。 greatest将返回这两个时间戳中的最新一个。如果第一个参数表示时间而不是7:00,则将返回该参数。如果没有,将返回第二个参数(即7:00)。

外部greatest调用如下所示:

greatest(0, time_to_sec(timediff(....)))

这将确保时差不是负数。拿这个例子记录:

   login_time   |   logoff_time
----------------+----------------
2017-06-01 6:30 | 2017-06-01 6:45

在这种情况下,最里面的greatest会返回2017-06-01 7:00,因为6:30太早了。但这会使timediff()返回否定时间间隔:-15分钟。我们真正想要的是0,因为用户在 7:00之后没有时间登录。这是greatest将执行的操作:greatest(0, -15) = 0,因此负值将被消除,并且不会影响总和。

login_time

上的条件

我将条件time(login_time) between "00:00:00" and "23:59:59"留在那里,但它确实没有做任何事情,因为这一切都是正确的(除非它们是null,但它们也不会通过第一个条件)

在新要求之后编辑

在评论中,您询问如何在用户未在同一天注销但在1或2天后保持在线状态的每一天进行分组。

在这种情况下,您需要一个帮助程序表,列出您希望在输出中查看的所有日期。例如,这可能是最后7天的七个记录。

然后你必须加入你的表,以便至少有一个用户会话与这样一个参考日期的重叠。在线时间的计算必须考虑到注销时间可能不在午夜之前。

以下是更新的查询:

select   ref_date date,
         time_format(sec_to_time(sum(greatest(0, time_to_sec(timediff(
             least(ifnull(logoff_time, now()), date_add(ref_date, interval 1 day ), now()), 
             greatest(login_time,              date_add(ref_date, interval 7 hour))
         ))))), '%H:%i:%s') online
from     (  select date(date_add(now(), interval (-6) DAY)) as ref_date union all 
            select date(date_add(now(), interval (-5) DAY)) union all 
            select date(date_add(now(), interval (-4) DAY)) union all 
            select date(date_add(now(), interval (-3) DAY)) union all 
            select date(date_add(now(), interval (-2) DAY)) union all 
            select date(date_add(now(), interval (-1) DAY)) union all 
            select date(now())
         ) ref
inner join tb_sessions 
        on login_time < date_add(ref_date, interval 1 day)
       and logoff_time > date_add(ref_date, interval 7 hour) 
where    user_id = 3098
group by ref_date

sqlfiddle上看到它。