Объединить каждые 2 последовательных записи в 1 - PullRequest
0 голосов
/ 17 марта 2020

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

|-------------------|-----|----|
|Timestamp          |Event|User|
|-------------------|-----|----|
|17/03/2020 03:22:00|Start|1   |
|17/03/2020 03:22:05|End  |1   |
|17/03/2020 03:22:10|Start|2   |
|17/03/2020 03:22:15|End  |2   |
|17/03/2020 03:23:00|Start|1   |
|17/03/2020 03:23:22|End  |1   |
|-------------------|-----|----|

Запрос должен вернуть:

|-------------------|-------------------|----|
|StartTimestamp     |EndTimestamp       |User|
|-------------------|-------------------|----|
|17/03/2020 03:22:00|17/03/2020 03:22:05|1   |
|17/03/2020 03:22:10|17/03/2020 03:22:15|2   |
|17/03/2020 03:23:00|17/03/2020 03:23:22|1   |
|-------------------|-------------------|----|

Можно смело предположить, что каждые 2 записи являются правильной парой (события начинаются и заканчиваются соответственно, а пользователь совпадает) , поскольку таблица предварительно отфильтрована.

РЕДАКТИРОВАТЬ: Извините, я забыл упомянуть, что допускается наличие нескольких пар для одного пользователя. Я изменил приведенный выше пример таблицы, чтобы показать это.

Ответы [ 4 ]

2 голосов
/ 17 марта 2020

Как предложено, это должно делать то, что вы хотите:

SELECT
     MIN(Timestamp) AS StartTimestamp,
     MAX(Timestamp) AS EndTimestamp,
     User
FROM 
     mytable
GROUP BY User;

РЕДАКТИРОВАТЬ: поскольку идентификатор пользователя может появляться несколько раз, в нескольких группах, см. Следующий запрос:

WITH cte AS (
     SELECT mt.*, ROW_NUMBER() OVER(ORDER BY time) AS rn FROM mytable mt
)
SELECT 
     t1.userid,
     t1.time AS StartTimestamp, 
     t2.time AS EndTimestamp
FROM cte t1
JOIN cte t2 ON t1.rn+1 = t2.rn
WHERE t1.event = 'Start'

С ДЕМО ЗДЕСЬ

1 голос
/ 17 марта 2020

Я бы предложил использовать lead() или совокупный min():

select t.*
from (select t.*,
             min(case when event = 'End' then timestamp end) over (partition by user order by timestamp desc) as end_time
      from t
     ) t
where event = 'Start';
1 голос
/ 17 марта 2020

Вы можете использовать row_number() и сделать условное агрегирование:

select user, 
       min(case when event = 'Start' then timestamp end) as starttimestamp,
       min(case when event = 'End' then timestamp end) as endtimestamp
from (select t.*, 
             row_number() over (partition by user, event order by timestamp) as seq
      from table t
     ) t
group by user, seq;
0 голосов
/ 17 марта 2020

Количество строк на пользователя и событие, чтобы добраться до номеров событий. Затем событие присоединения начинается с окончания события.

with s as
(
  select
    [user], timestamp,
    row_number() over (partition by [user] order by timestamp) as event_number
  from mytable
  where event = 'Start'
)
, e as
(
  select
    [user], timestamp,
    row_number() over (partition by [user] order by timestamp) as event_number
  from mytable
  where event = 'End'
)
select s.[user], s.timestamp as start_time, e.timestamp as end_time
from s
join e on e.[user] = s.[user] and e.event_number = s.event_number
order by start_time;

Используйте левое внешнее объединение, если вы хотите показать события, которые начались, но еще не завершились.

Этот запрос также допускает параллельные события (т. е. пользователь запускает событие, затем другой пользователь запускает событие до того, как первый пользователь заканчивает свое).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...