2017-02-22 39 views
1

У меня есть таблица событий, генерируемая активности пользователей на сайте:Группировка Событие в Postgres

timestamp | name 
7:00 AM | ... 
7:01 AM | ... 
7:02 AM | ... 
7:30 AM | ... 
7:31 AM | ... 
7:32 AM | ... 
8:01 AM | ... 
8:03 AM | ... 
8:05 AM | ... 
8:08 AM | ... 
8:09 AM | ... 

Я хотел бы объединить через событие, чтобы обеспечить представление о том, когда пользователь активен , Я определяю активный, чтобы означать период, в течение которого событие находится в пределах +/- 2 минут. Для выше, что бы означать:

from | till 
7:00 AM | 7:02 AM 
7:30 AM | 7:32 AM 
8:01 AM | 8:05 AM 
8:08 AM | 8:09 AM 

Какой самый лучший способ, чтобы написать запрос, который будет агрегировать в этом методе? Возможно ли это с помощью функции WINDOW или самостоятельного присоединения или требуется PL/SQL?

ответ

1

Использование два оконных функции: один для расчета интервалов между смежными событиями (пробелов), а другой - для поиска серии зазоров менее или равных 2 минут:

select arr[1] as "from", arr[cardinality(arr)] as "till" 
from ( 
    select array_agg(timestamp order by timestamp) arr 
    from (
     select timestamp, sum((gap > '2m')::int) over w 
     from (
      select timestamp, coalesce(timestamp - lag(timestamp) over w, '3m') gap 
      from events 
      window w as (order by timestamp) 
      ) s 
     window w as (order by timestamp) 
     ) s 
    group by sum 
    ) s 

    from | till 
----------+---------- 
07:00:00 | 07:02:00 
07:30:00 | 07:32:00 
08:01:00 | 08:05:00 
(3 rows)   

Test it here.

0

группируя их вокруг получасового пола и получение мин & максимальных значений:

WITH x(t) AS (VALUES 
('7:02 AM'::TIME),('7:01 AM'::TIME),('7:00 AM'::TIME), 
('7:30 AM'::TIME),('7:31 AM'::TIME),('7:32 AM'::TIME), 
('8:01 AM'::TIME),('8:03 AM'::TIME),('8:05 AM'::TIME) 
) 
SELECT MIN(t) "from", MAX(t) "till" 
    FROM (select t, date_trunc('hour', t) + 
     CASE WHEN (t-date_trunc('hour', t)) >= '30 minutes'::interval 
     THEN '30 minutes'::interval ELSE '0'::interval END t1 FROM x) y 
    GROUP BY t1 ORDER BY t1; 

Вы можете применить тот же прием со значениями даты и времени, как:

WITH x(t) AS (
    SELECT '2017-01-01'::TIMESTAMP + (RANDOM()*1440*'1 minute'::INTERVAL) t 
    FROM GENERATE_SERIES(0,1000)) 
SELECT MIN... 
+0

Это не отвечает на вопрос. У меня нет гарантии, что сегменты всегда попадают в 30-минутные интервалы (добавлен пример счетчика). – Stussa