如何使用雪花SQL根据时间戳记间隔合并行?

时间:2020-08-05 20:55:13

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

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

accounts

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

示例表的输出应为:

{
  "properties": {
    "name": {
      "type": "text"
    },
    "number": {
      "type": "text"
    }
  }
}

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

2 个答案:

答案 0 :(得分: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
    

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

答案 1 :(得分:0)

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