如何使用雪花 sql 基于时间戳间隔合并行?

How to combine rows based on timestamp intervals using snowflake sql?

我有一个 table 存储用户 ID、会话 ID 和日期时间。 table 存储用户登录设备时的数据,并存储用户、会话和日期时间。一个 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 

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

示例 table 的输出应该是:

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 中的新 table 中,但这会非常慢并且无法扩展。提前致谢。

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

这需要某种迭代过程。在 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
    

第一个 common-table-expression (data) 枚举 useridsessionidrow_number() 相同的行。然后,第二个 CTE (cte) 从第一行开始迭代遍历行组,并根据需要创建新组。最后一步是聚合。

此方法利用了 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
 ;