Как объединить строки на основе интервалов временных меток, используя снежинку sql? - PullRequest
2 голосов
/ 05 августа 2020

У меня есть таблица, в которой хранятся идентификатор пользователя, идентификатор сеанса и дата и время. В таблице хранятся данные, когда пользователь входит в систему, а также пользователь, сеанс и дата и время. Может быть несколько записей с одним сочетанием идентификатора пользователя и идентификатора сеанса. Например:

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 

Я пытаюсь объединить эти строки в новую таблицу на основе начального datetime до datetime + x для того же сеанса и пользователя. Начальное datetime перемещается, если дата превышает datetime + x. Итак, если x равен 30 минутам, любая дата от начала до datetime + 30min будет одной строкой. Если дата больше, чем datetime + 30min, она становится новой start datetime, и вы выполняете datetime + x до тех пор, пока все даты не будут просмотрены для комбинации идентификатора сеанса и идентификатора пользователя.

Результат таблицы примера должен быть :

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. Я собирался создать хранимую процедуру для выполнения всех logi c в javascript, а затем вставить в новую таблицу в Snowflake, но это будет очень медленно и не масштабируется. Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 06 августа 2020

Это немного сложно. Вы не можете просто сравнивать соседние строки: вам нужно отслеживать дату начала каждой серии строк, чтобы вы могли сравнивать ее со следующими датами и решать, когда разбивать на новую группу.

Это требует некоторых своего рода итеративный процесс. В 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) перечисляет строки, имеющие одинаковые userid и sessionid с row_number(). Затем второй CTE (cte) итеративно обходит группы строк, начиная с первой, и при необходимости создает новые группы. Последний шаг - агрегация.

0 голосов
/ 07 августа 2020

Этот метод использует функцию 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
 ;
...