Фон
Я пытаюсь преобразовать этот запрос mysql для работы с базой данных, в которой нет переменных, которые могут быть установлены на уровне строк, как вы можете с помощью mysql.Я не уверен, что это можно сделать без циклов, но это цель.
Проблема
У нас есть идентификатор клиента id
и метка времени сеанса event_datetime
.
Для каждого клиента мне нужно интерпретировать каждый сеанс как действительный или недействительный согласно следующему определению:
- срок действия действующего сеанса истекает через 30 минут.
- сеанс действителен, если он не произошел во время более раннего действительного сеанса.
Альтернативное определение тривиально для вычисления: сеанс действителен, если он был 30минут с последнего сеанса.Но это не то, что мне нужно здесь.
Так, например:
2018-01-01 00:00:00 <-- valid
2018-01-01 00:15:00 <-- invalid
2018-01-01 00:31:00 <-- valid
2018-01-01 01:14:00 <-- valid
2018-01-01 01:17:00 <-- invalid
2018-01-01 01:25:00 <-- invalid
2018-01-01 01:43:00 <-- invalid
2018-01-01 01:45:00 <-- valid
Я просто пытаюсь избежать зацикливания.Использование любых общедоступных аналитических / оконных функций прекрасно.В конечном счете, я пытаюсь реализовать это на снежинке.
То, что я пытался
Я пытался придумать что-то, используя оконные функции, объединения, не существует, ноизо всех сил, чтобы найти решение.Например, выполнение промежуточной суммы различий во времени сеанса казалось многообещающим, но я не мог придумать, как заставить накопленную сумму обнуляться после достижения 30 минут.Я знаю, что мог бы заказать сеансы каждого клиента и выполнить цикл (так, чтобы максимальные итерации были максимальным числом сеансов для одного клиента), но пытался избежать этого.
Пример данных и решение MySQL
Ниже приведено решение, использующее mysql.Оба определения рассчитаны (по истечении 30 минут и 30 минут).
DROP TABLE IF EXISTS work.test;
CREATE TABLE work.test (id INT, event_datetime DATETIME);
INSERT INTO work.test
VALUES (123456789, '2017-12-08 15:24:29.297000000'),
(123456789, '2017-12-08 15:25:42.510000000'),
(123456789, '2017-12-08 15:28:49.023000000'),
(123456789, '2017-12-10 07:23:49.693000000'),
(123456789, '2017-12-10 07:25:03.487000000'),
(123456789, '2017-12-10 07:35:52.613000000'),
(123456789, '2017-12-10 07:45:52.613000000'),
(123456789, '2017-12-10 07:55:52.613000000'),
(123456789, '2017-12-10 08:05:52.613000000'),
(123456789, '2017-12-10 15:55:24.070000000'),
(123456789, '2017-12-10 15:55:57.063000000'),
(123456789, '2017-12-10 15:56:37.633000000'),
(123456789, '2017-12-17 09:00:41.543000000'),
(123456789, '2017-12-17 09:02:13.187000000'),
(123456789, '2017-12-17 09:02:47.370000000'),
(123456789, '2017-12-17 09:03:29.843000000'),
(123456789, '2017-12-17 09:03:56.667000000'),
(123456789, '2017-12-17 09:06:12.493000000'),
(123456789, '2017-12-17 09:07:26.113000000');
SELECT
@last_session_datetime AS last_session_datetime,
@diff := timestampdiff(MINUTE, @last_session_datetime, s.event_datetime) AS diff,
if(@diff IS NULL OR @diff >= 30, 'valid', 'not valid') AS valid_30_minute_lapse,
@last_visit_datetime := if(@curr_customer_id = s.id AND timestampdiff(MINUTE, @last_visit_datetime, s.event_datetime) < 30, @last_visit_datetime, s.event_datetime) AS last_visit_datetime,
if(@last_visit_datetime = s.event_datetime, 'valid', 'not valid') AS valid_30_minute_expiration,
@curr_customer_id := s.id,
id,
event_datetime,
@last_session_datetime := s.event_datetime
FROM work.test s
JOIN (
SELECT
@curr_customer_id := 0,
@last_visit_datetime := '1900-01-01',
@last_session_datetime := NULL) a
ORDER BY s.id, s.event_datetime
В этих примерах данных сеанс 2017-12-10 07:55:53
будет действителен по истечении 30 минут, но недействителен по истечении 30-минутного промежутка.Прошло всего 10 минут после предыдущего сеанса, но прошло более 30 минут с момента последнего подтвержденного сеанса.