【问题标题】:Grouping Timestamps based on the interval between them根据时间戳之间的间隔对时间戳进行分组
【发布时间】:2015-03-07 15:36:45
【问题描述】:

我在 Hive (SQL) 中有一个表,其中包含一堆需要分组的时间戳,以便根据时间戳之间的时间差创建单独的会话。

示例: 考虑以下时间戳(为简单起见,以 HH:MM 给出): 9.00 9.10 9.20 9.40 9.43 10.30 10.45 11.25 12.30 12.33 等等..

所以现在,在下一个时间戳 30 分钟内的所有时间戳都属于同一个会话, 即 9.00,9.10,9.20,9.40,9.43 形成 1 个会话。

但由于 9.43 和 10.30 之间的差异超过 30 分钟,因此时间戳 10.30 属于不同的会话。同样,10.30 和 10.45 属于一个会话。

创建这些会话后,我们必须获取该会话的最小时间戳和最大时间戳。

我尝试用它的 LEAD 减去当前时间戳,如果它大于 30 分钟,则放置一个标志,但我遇到了困难。

你们的任何建议将不胜感激。如果问题不够清楚,请告诉我。

此示例数据的预期输出:

Session_start   Session_end
9.00                9.43
10.30               10.45
11.25               11.25 (same because the next time is not within 30 mins)
12.30               12.33

希望这会有所帮助。

【问题讨论】:

  • 请发布您的表结构以及示例数据和预期输出
  • @Akhil 请立即查看
  • @FenderBender 我们已经检查过了。没有变化。

标签: sql session select group-by hive


【解决方案1】:

所以不是 MySQL,而是 Hive。我不知道 Hive,但如果它支持 LAG,如你所说,试试这个 PostgreSQL 查询。您可能必须更改时差计算,这通常因一个 dbms 与另一个不同。

select min(thetime) as start_time, max(thetime) as end_time
from
(
  select thetime, count(gap) over (rows between unbounded preceding and current row) as groupid
  from
  (
    select thetime, case when thetime - lag(thetime) over (order by thetime) > interval '30 minutes' then 1 end as gap
    from mytable
  ) times
) groups
group by groupid
order by min(thetime);

查询找到间隙,然后使用间隙计数的总和来构建组 ID,剩下的就是聚合。

SQL 小提琴:http://www.sqlfiddle.com/#!17/8bc4a/6.

【讨论】:

    【解决方案2】:

    由于 MySQL 缺少 LAG 和 LEAD 函数,因此获取上一条或下一条记录已经是一些工作了。方法如下:

    select 
      thetime, 
      (select max(thetime) from mytable afore where afore.thetime < mytable.thetime) as afore_time,
      (select min(thetime) from mytable after where after.thetime > mytable.thetime) as after_time
    from mytable;
    

    在此基础上,我们可以构建整个查询来寻找差距(即与上一条或下一条记录的时间差超过 30 分钟 = 1800 秒)。

    select
      startrec.thetime as start_time,
      (
        select min(endrec.thetime) 
        from 
        (
          select 
            thetime, 
            coalesce(time_to_sec(timediff((select min(thetime) from mytable after where after.thetime > mytable.thetime), thetime)), 1801) > 1800 as gap
          from mytable
        ) endrec
        where gap
        and endrec.thetime >= startrec.thetime
      ) as end_time
    from
    (
      select 
        thetime, 
        coalesce(time_to_sec(timediff(thetime, (select max(thetime) from mytable afore where afore.thetime < mytable.thetime))), 1801) > 1800 as gap
      from mytable
    ) startrec
    where gap;
    

    SQL 小提琴:http://www.sqlfiddle.com/#!2/d307b/20.

    【讨论】:

    • 非常感谢您的全面回答。我们实际上可以使用领先和滞后,对于这种混乱,我深表歉意。
    【解决方案3】:

    试试这个..

    SELECT MIN(session_time_tmp) session_start, MAX(session_time_tmp) session_end FROM 
    (
    SELECT  IF((TIME_TO_SEC(TIMEDIFF(your_time_field, COALESCE(@previousValue, your_time_field))) / 60) > 30 , 
            @sessionCount := @sessionCount + 1, @sessionCount ) sessCount, 
            ( @previousValue := your_time_field ) session_time_tmp  FROM 
    (
    SELECT your_time_field, @previousValue:= NULL, @sessionCount := 1 FROM yourtable ORDER BY your_time_field
    ) a
    ) b
    GROUP BY sessCount
    

    只需替换 yourtableyour_time_field

    【讨论】:

    • 哇,变量的使用令人印象深刻。我应该练习这个:-) 不过,如果你解释了你的查询在做什么,你的答案会更好。
    • 谢谢@ThorstenKettner。很抱歉没有解释。事实是我试图解释,但未能有效地做到这一点!所以我会通过提取每个查询来推荐 learn_it_yourself 方法:)
    【解决方案4】:

    试试这个:

    SELECT DATE_FORMAT(MIN(STR_TO_DATE(B.column1, '%H.%i')), '%H.%i') AS Session_start, 
           DATE_FORMAT(MAX(STR_TO_DATE(B.column1, '%H.%i')), '%H.%i') AS Session_end
    FROM tableA A
    LEFT JOIN ( SELECT A.column1, diff, IF(@diff:=diff < 30, @id, @id:=@id+1) AS rnk
                FROM (SELECT B.column1, TIME_TO_SEC(TIMEDIFF(STR_TO_DATE(B.column1, '%H.%i'), STR_TO_DATE(A.column1, '%H.%i'))) / 60 AS diff
                      FROM tableA A
                      INNER JOIN tableA B ON STR_TO_DATE(A.column1, '%H.%i') < STR_TO_DATE(B.column1, '%H.%i') 
                      GROUP BY STR_TO_DATE(A.column1, '%H.%i')
                     ) AS A, (SELECT @diff:=0, @id:= 1) AS B
               ) AS B ON A.column1 = B.column1
    GROUP BY IFNULL(B.rnk, 1);
    

    查看SQL FIDDLE DEMO

    输出

    | SESSION_START | SESSION_END |
    |---------------|-------------|
    |          9.00 |        9.43 |
    |         10.30 |       10.45 |
    |         11.25 |       11.25 |
    |         12.30 |       12.33 |
    

    【讨论】:

    • 非常感谢您的回复。但恐怕在这种情况下这不起作用:` 9.00 9.03 9.10 9.55 10.00` 现在,9.00 到 9.10 是一个会话,9.55 到 10.00 是另一个会话,因为 9.10 到 9.55 之间的差异超过 30 分钟。
    • @SaharshShah。你可以自己检查一下。 sqlfiddle.com/#!2/bb346/1 。我所做的只是将 10 30 更改为 10 05。伙计,我对 Pankaj 的看法并不是要让你失望,但在这里为选票做营销是错误的 :-)。希望你能得到它
    • @Akhil 如果有人做得很好并花时间在这里,我们必须感谢并给他加分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    • 2012-12-13
    • 1970-01-01
    • 1970-01-01
    • 2018-07-08
    • 2014-07-12
    • 2020-11-02
    相关资源
    最近更新 更多