Чтобы проиллюстрировать подход, используя данные из вашего примера, вот как запрос бы который показывает новые сеансы с временем начала выглядеть следующим образом:
select user, ts start_time from (
select user, ifnull(seconds - prev_seconds > 10, true) new_session from (
select user, ts, seconds, lag(seconds, 1) over(partition by user order by seconds) prev_seconds from
(select user, ts, integer(ts/1000000) seconds from
(select 'user_a' user, timestamp('2015-06-15 14:12:12') ts),
(select 'user_b' user, timestamp('2015-06-15 14:12:12') ts),
(select 'user_a' user, timestamp('2015-06-15 14:12:13') ts),
(select 'user_a' user, timestamp('2015-06-15 14:12:19') ts),
(select 'user_a' user, timestamp('2015-06-15 14:12:28') ts),
(select 'user_a' user, timestamp('2015-06-15 19:32:15') ts),
(select 'user_a' user, timestamp('2015-06-15 19:32:19') ts))))
where new_session
также получить продолжительности сессии, а не делать автообъединение, мы можем запустить другую функцию окна. В основном мы находим первое начало и конец сессии, а затем вычислить разницу между ними:
select user, ts, if(next_is_last, next_seconds - seconds, 0) duration
from (
select
user, new_session, last_session, ts, seconds,
lead(seconds, 1) over(partition by user order by seconds) next_seconds,
lead(last_session, 1) over(partition by user order by seconds) next_is_last
from (
select
user,
ts,
ifnull(seconds - prev_seconds > 10, true) new_session,
ifnull(next_seconds - seconds > 10, true) last_session
from (
select
user,
ts,
seconds,
lag(seconds, 1) over(partition by user order by seconds) prev_seconds,
lead(seconds, 1) over(partition by user order by seconds) next_seconds
from
(select user, ts, integer(ts/1000000) seconds from
(select 'user_a' user, timestamp('2015-06-15 14:12:12') ts),
(select 'user_b' user, timestamp('2015-06-15 14:12:12') ts),
(select 'user_a' user, timestamp('2015-06-15 14:12:13') ts),
(select 'user_a' user, timestamp('2015-06-15 14:12:19') ts),
(select 'user_a' user, timestamp('2015-06-15 14:12:28') ts),
(select 'user_a' user, timestamp('2015-06-15 19:32:15') ts),
(select 'user_a' user, timestamp('2015-06-15 19:32:19') ts))))
where new_session or last_session)
where new_session
Это приводит к:
Row user ts duration
1 user_a 2015-06-15 14:12:12 UTC 16
2 user_a 2015-06-15 19:32:15 UTC 4
3 user_b 2015-06-15 14:12:12 UTC 0
Спасибо за Ваш ответ. Возможно, я описал свой вопрос непонятным. Вместо того, чтобы подсчитывать каждый интервал доступа в качестве сеанса, я хочу конкатрировать все непрерывные интервалы в один сеанс, если они не нарушены интервалом таймаута. Поэтому 'user_a' в моем примере имеет только два сеанса. Первые три интервала объединены в один сеанс, который длится 16 секунд. –
Если вы запустите запрос выше - вы получите именно тот результат, который вы описали - user_a будет иметь два сеанса. –
О, извините, я выполнил только подзапрос верхнего запроса. Вы правы, для user_a есть две сессии. Но мне действительно нужны длительности. Мне удалось генерировать каждый интервал доступа и являются ли они началом сеанса, но я не знаю, как суммировать продолжительность каждого сеанса. Любая идея? –