【问题标题】:How to combine rows based on timestamp intervals using snowflake sql?如何使用雪花 sql 根据时间戳间隔组合行?
【发布时间】:2020-08-05 20:55:13
【问题描述】:

我有一个存储用户 ID、会话 ID 和日期时间的表。该表存储用户登录到设备时的数据并存储用户、会话和日期时间。一个 userid 和 sessionid 组合可以有多个条目。例如:

USERID | SESSIONID | DATE 
abcd   | 1234      | 2020-05-14 10:30:00 
abcd   | 1234      | 2020-05-14 10:32:00 
abcd   | 1234      | 2020-05-14 10:35:00 
abcd   | 1234      | 2020-05-14 11:32:00 
abcd   | 1234      | 2020-05-14 11:39:00 

我正在尝试将这些行组合到一个新表中,该表基于相同会话和用户的初始日期时间到日期时间 + x。如果日期超过 datetime+x,则初始日期时间会移动。因此,如果 x 是 30 分钟,则从 start 到 datetime + 30min 的任何日期都是一行。如果某个日期大于 datetime + 30 分钟,它将成为新的开始日期时间,并且您执行 datetime+x 直到所有日期都被查看为 sessionid 和 userid 组合。

示例表的输出应该是:

USERID | SESSIONID | START_SESSION_DATE  | END_SESSION_DATE
abcd   | 1234      | 2020-05-14 10:30:00 | 2020-05-14 10:35:00 
abcd   | 1234      | 2020-05-14 11:32:00 | 2020-05-14 11:39:00 

我不确定如何仅使用 SQL 来实现这一点。我打算创建一个存储过程来执行 javascript 中的所有逻辑,然后插入到 Snowflake 中的新表中,但这会非常慢并且不会扩展。提前致谢。

【问题讨论】:

    标签: sql datetime snowflake-cloud-data-platform recursive-query


    【解决方案1】:

    这有点棘手。您不能只比较相邻的行:您需要跟踪每一系列行的开始日期,以便将其与以下日期进行比较,并决定何时分入新组。

    这需要某种迭代过程。在 SQL 中,这通常通过递归查询来实现 - 幸运的是,Snowflake 支持

    考虑:

    with recursive 
        data as (
            select 
                t.*, 
                row_number() over(partition by userid, sessionid order by date) rn
            from mytable t
        ),
        cte as (
            select 
                userid, 
                sessionid,
                date start_session_date,
                date end_session_date
            from data
            where rn = 1
            union all
            select
                c.userid,
                c.sessionid,
                case when d.date > dateadd(minute, 30, c.start_session_date)
                    then d.date
                    else c.start_session_date
                end,
                d.date
            from cte c
            inner join data d 
                on  d.userid = c.userid
                and d.sessionid = c.sessionid 
                and d.rn = c.rn + 1 and 
        )
    select 
        userid, 
        sessionid, 
        start_session_date, 
        max(end_session_date) end_session_date
    from cte
    group by userid, sessionid, start_session_date
        
    

    第一个公共表表达式 (data) 枚举具有相同 useridsessionidrow_number() 的行。然后,第二个 CTE (cte) 从第一个开始迭代遍历行组,并根据需要创建新组。最后一步是聚合。

    【讨论】:

    • 我终于让它工作了。我仍然需要看看这一切是如何工作的,因为我不完全理解它,但谢谢你这有很大帮助。
    【解决方案2】:

    此方法使用 Snowflake WIDTH_BUCKET 函数,根据我生成的一些测试数据,它似乎可以正常工作:

    -- Get the min amd max timestamps for each userid, sessionid
    WITH T1 AS (    
    SELECT USERID, SESSIONID,MIN(DATE_TIME) MIN_DATE, MAX(DATE_TIME) MAX_DATE
    FROM TEST_DATA
    GROUP BY USERID, SESSIONID
    ),
    --Get the number of 'buckets', for each userid/sessionid, to divide the data into by defining the time period
    --Hardcoded here as MINUTE and 30
    T2 AS (
    SELECT USERID, SESSIONID, MIN_DATE, MAX_DATE, CEIL(DATEDIFF(MINUTE, MIN_DATE, MAX_DATE)/30,0) NUM_BUCKETS    
    FROM T1
     ),
    --Assign each record to the appropriate time period bucket
    --WIDTH_BUCKET takes numeric parameters hence the conversion to epoch_seconds
    T3 AS (
    SELECT TD.USERID, TD.SESSIONID, TD.DATE_TIME
    ,width_bucket(DATE_PART(EPOCH_SECONDS,TD.DATE_TIME), DATE_PART(EPOCH_SECONDS,T2.MIN_DATE), DATE_PART(EPOCH_SECONDS,T2.MAX_DATE), T2.NUM_BUCKETS) as "TIME_GROUP"
    FROM TEST_DATA TD
    INNER JOIN T2 ON TD.USERID = T2.USERID AND TD.SESSIONID = T2.SESSIONID
    )
    --Get the min and make timestamps for each userid, sessionid and bucket combination
    SELECT USERID, SESSIONID, MIN(DATE_TIME), MAX(DATE_TIME)
    FROM T3
    GROUP BY USERID, SESSIONID, TIME_GROUP
    order BY USERID, SESSIONID, TIME_GROUP
    LIMIT 10
     ;
    

    【讨论】:

      猜你喜欢
      • 2015-03-07
      • 2021-03-08
      • 2018-04-28
      • 1970-01-01
      • 2021-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-21
      相关资源
      最近更新 更多