🖥 Задача. Подсчет сессий пользователя.



Не знаю почему на каждом 2 собеседовании любят давать эту задачу, в разных вариациях, тем не менее суть одна. Решений эта задача имеет также не мало, но мы попробуем решить ее через оконные функции, как мой самый любимый способ решения. Возьмем один из самых простых вариантов задачи, для каждого юзера мы имеем лог его данных который снимаем каждые 5 минут, если разница между двумя промежутками больше, то это новая сессия, нужно разметить каждую сессию.



+------+----------------------------+

| user | login |

+------+----------------------------+

| 1 | 2022-10-12 20:05:00.000000 |

| 1 | 2022-10-12 20:10:00.000000 |

| 1 | 2022-10-12 20:15:00.000000 |

| 1 | 2022-10-12 20:20:00.000000 |

| 1 | 2022-10-12 20:25:00.000000 |

| 1 | 2022-10-12 10:05:00.000000 |

| 1 | 2022-10-12 20:40:00.000000 |

| 1 | 2022-10-12 20:30:00.000000 |

| 1 | 2022-10-12 10:20:00.000000 |

| 1 | 2022-10-12 10:10:00.000000 |

| 1 | 2022-10-12 20:05:00.000000 |

| 2 | 2022-10-12 20:10:00.000000 |

| 2 | 2022-10-12 20:15:00.000000 |

| 2 | 2022-10-12 20:20:00.000000 |

| 2 | 2022-10-12 20:25:00.000000 |

| 2 | 2022-10-12 10:05:00.000000 |

| 2 | 2022-10-12 20:40:00.000000 |

| 2 | 2022-10-12 20:30:00.000000 |

| 2 | 2022-10-12 10:20:00.000000 |

| 2 | 2022-10-12 10:10:00.000000 |

+------+----------------------------+



Впервую очередь отсортируем данные и применим функцию lag для смещения данных на 1 значение вниз



SELECT t1.user,

login,

lag(login, 1, NULL) OVER

(PARTITION BY

t1.user ORDER BY t1.user,

login) next_log

FROM table t1


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



select *,

sum(ind) over

(PARTITION BY t3.user

ORDER BY t3.user rows

between unbounded preceding and current row) as sessions

from (

select t2.user,

login,

case

when (login-prev_log) > '5 mins' then 1

else 0

end ind

from (

SELECT t1.user,

login,

lag(login, 1,NULL)

OVER (PARTITION BY t1.user

ORDER BY t1.user, login) prev_log

FROM table t1) t2

) t3



@sqlhub