MySQL选择时间分组,其中时间戳与不同行重叠,具有时区差异

时间:2015-01-13 18:18:09

标签: php mysql

这个问题似乎与其他问题不同,所以我会在这里问一下。

我有一个存储时间戳和时间戳的MySQL表,我想从这个表中选择分组来计算人们同时“在线”的组。这种疯狂背后的想法是在相交的时间段内自动将人们聚集在一起。理想情况下,为这个小组获得最佳时间会很棒(但这可能是不可能的)。

我有两个表,一个名为“times”的表存储时间,一个名为“users”的表存储用户详细信息,users表还包含应该应用于时间的时差字段(以小时为单位)(所有时间都以UTC格式存储。

以下是我的表格:

Users
userid | timediff
------------------
1      | 0
2      | 0
3      | 1
4      | 4
5      | -8
6      | 2
7      | 2

Times
userid | from                | to 
1      | 2015-01-13 16:00:00 | 2015-01-13 23:00:00
2      | 2015-01-13 13:00:00 | 2015-01-13 21:00:00
3      | 2015-01-13 14:00:00 | 2015-01-13 22:00:00
4      | 2015-01-13 11:00:00 | 2015-01-13 12:00:00
5      | 2015-01-13 10:00:00 | 2015-01-13 12:00:00
6      | 2015-01-13 11:00:00 | 2015-01-13 12:00:00
7      | 2015-01-13 09:00:00 | 2015-01-13 10:00:00   

在一个完美的世界中,这会将人们聚集在一起:

1      | 2015-01-13 16:00:00 | 2015-01-13 23:00:00
2      | 2015-01-13 13:00:00 | 2015-01-13 21:00:00
3      | 2015-01-13 14:00:00 | 2015-01-13 22:00:00

这些人在16:00 - 21:00之间在线一起

4      | 2015-01-13 11:00:00 | 2015-01-13 12:00:00
5      | 2015-01-13 10:00:00 | 2015-01-13 12:00:00
6      | 2015-01-13 11:00:00 | 2015-01-13 12:00:00

这些人在11:00 - 12:00之间一起在线

(另请注意,这并不考虑时间差异以便于理解,但如果需要另外,我很乐意解决这个问题。)

这可能是不可能只用sql我可能需要使用PHP,我没有发布任何示例代码,因为我不确定最好的方向,任何指针都会很棒!

1 个答案:

答案 0 :(得分:1)

这不是一个超级简单的项目。它有很多部分,特别是时区偏移,时间范围比较和巧合搜索。

但是,让我们试一试。首先,让我们创建一个视图来处理时区偏移的东西。我们真的不想一遍又一遍地捣乱。这个观点就是这样做的。

CREATE VIEW `utctimes` 
    AS select `t`.`userid` AS `userid`,
              `t`.`from` AS `from`,
              `t`.`to` AS `to`,
              `t`.`from` + interval `u`.`timediff` hour AS `utcfrom`,
              `t`.`to` + interval `u`.`timediff` hour AS `utcto`
         from `times` `t` 
         join `users` `u` on `u`.`userid` = `t`.`userid`;

接下来,让我们自我加入此视图并进行一些时间范围比较,以找出何时有多个人在线。要查看一对from / to范围是否重叠,这个逻辑就可以了。

    a.from <= b.to
and b.from <= a.to

如果这两个条件都成立,你可以说服自己两个范围重叠。

我们假设两个人都在线,即使其中一个正好在正午,而另一个正好在正午,但即使这可能是一个不好的假设。

此查询将在每个时间范围内的某个时间为我们提供时间范围列表和在线用户数。它通过混杂的(因此有点贵)自我加入来做到这一点。

select count(*) as users_on, 
       greatest(a.utcfrom, b.utcfrom) utcfrom, 
       least(a.utcto, b.utcto) utcto
  from utctimes a
  join utctimes b on a.userid <> b.userid
 where a.utcfrom <= b.utcto
   and b.utcfrom <= a.utcto
 group by  greatest(a.utcfrom, b.utcfrom), least(a.utcto, b.utcto) 
 order by count(*) desc, 
          greatest(a.utcfrom, b.utcfrom),
          timestampdiff(minute, greatest(a.utcfrom, b.utcfrom), 
                       least(a.utcto, b.utcto)) desc

这将首先提供最受欢迎的范围,然后按流行度顺序提供其他一些范围。它确实产生了一些重叠的范围。

拥有最受欢迎的时间范围后,您可以找出哪些用户在这些范围内在线。例如,这个JOIN就是这样做的。

select r.users_on, r.utcfrom online_session_start, 
       timediff(r.utcto, r.utcfrom) online_session_duration,
       q.userid, q.`from`, q.`to`
  from utctimes q
  join (
    select count(*) as users_on, 
           greatest(a.utcfrom, b.utcfrom) utcfrom, 
           least(a.utcto, b.utcto) utcto
      from utctimes a
      join utctimes b on a.userid <> b.userid
     where a.utcfrom <= b.utcto
       and b.utcfrom <= a.utcto
     group by  greatest(a.utcfrom, b.utcfrom), least(a.utcto, b.utcto) 
        ) r on q.utcfrom <= r.utcto
           and r.utcfrom <= q.utcto
 order by 2,3,4