バックグラウンド
このmysqlクエリを変換して、mysqlでできるように、行レベルで設定できる変数を持たないデータベースで動作するように苦労しています。ループせずにこれを行うことが可能かどうかはわかりませんが、それが目標です。
問題
顧客 IDid
とセッションのタイムスタンプがあり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 分以上経過しています。