セッション化 — LAG で前イベント時刻を取得し、30分超ギャップで条件付き累積SUM によりセッションIDを採番
イベントモデリングで最頻出の前処理がセッション化(Sessionization)です。同一ユーザーの連続するイベントを、一定の非アクティブ時間(例: 30分)で区切ってセッションに束ねます。Window関数 LAG で直前イベント時刻を取得し、ギャップ判定の0/1フラグを累積SUM することでセッションIDが採番できます。
-- LAG(): 同一PARTITION内で1行前の値を取得 LAG(event_time) OVER (PARTITION BY user_id ORDER BY event_time) -- 累積SUM でセッションIDを採番(境界フラグの累積 = セッション番号) SUM(is_new_session) OVER (PARTITION BY user_id ORDER BY event_time)
user_events テーブルから、同一ユーザーの直前イベントから30分超のギャップがあれば新しいセッションとして区切り、ユーザーごとにセッション数・総イベント数・1セッションあたり平均イベント数を集計してください。出力列は user_id, session_count, total_events, avg_events_per_session(小数第1位)、user_id 昇順で返してください。
| user_id | event_time |
|---|---|
| 1 | 2024-01-10 10:00 |
| 1 | 2024-01-10 10:05 |
| 1 | 2024-01-10 10:20 |
| 1 | 2024-01-10 12:00 |
| 1 | 2024-01-10 12:10 |
| 2 | 2024-01-10 09:00 |
| 2 | 2024-01-10 09:15 |
| 2 | 2024-01-10 11:30 |
| 3 | 2024-01-11 14:00 |
| user_id | session_count | total_events | avg_events_per_session |
|---|---|---|---|
| 1 | 2 | 5 | 2.5 |
| 2 | 2 | 3 | 1.5 |
| 3 | 1 | 1 | 1.0 |
アトリビューション — FIRST_VALUE + LAG で初回流入チャネルと購入直前タッチを抽出する
マーケティング分析の中核がアトリビューション(Attribution)です。あるコンバージョン(購入)が「どのチャネルの貢献によるものか」を割り当てる分析で、「初回タッチ」「ラストタッチ」「マルチタッチ」などのモデルがあります。Window関数で各ユーザーの行動履歴に「いつでも参照できる初回値」「直前の値」をぶら下げるのが基本テクニックです。
-- FIRST_VALUE: PARTITION 内の先頭行の値を全行に伝搬 FIRST_VALUE(channel) OVER (PARTITION BY user_id ORDER BY event_time) -- LAG: 直前1行の値を取得(購入直前タッチに利用) LAG(channel) OVER (PARTITION BY user_id ORDER BY event_time)
ORDER BY を指定すると、フレームの既定は RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW です。FIRST_VALUE は「フレームの先頭」の値を返すため、ORDER BY 指定があればパーティションの先頭値を全行に伝搬する挙動になり、初回タッチ抽出にそのまま使えます。user_events テーブルから、各購入(purchase)イベントに対して「そのユーザーの初回タッチチャネル(first_touch)」と「購入直前のチャネル(last_touch)」を抽出してください。出力列は user_id, first_touch, last_touch, purchase_time、user_id 昇順で返してください。購入のないユーザーは出力に含めません。
| user_id | event_time | event_type | channel |
|---|---|---|---|
| 1 | 2024-01-10 10:00 | view | google_ads |
| 1 | 2024-01-10 11:00 | view | organic |
| 1 | 2024-01-10 12:00 | click | facebook_ads |
| 1 | 2024-01-10 13:00 | purchase | direct |
| 2 | 2024-01-11 09:00 | view | organic |
| 2 | 2024-01-11 10:00 | purchase | |
| 3 | 2024-01-11 11:00 | view | google_ads |
| 3 | 2024-01-11 12:00 | click | google_ads |
| 4 | 2024-01-12 09:00 | view | twitter_ads |
| user_id | first_touch | last_touch | purchase_time |
|---|---|---|---|
| 1 | google_ads | facebook_ads | 2024-01-10 13:00 |
| 2 | organic | organic | 2024-01-11 10:00 |
ファネルステップ間時間 — LAG + EXTRACT(EPOCH) で平均コンバージョン所要時間を計算
基礎編で「ファネル到達数」「順序検証」を扱いましたが、実務では「ステップ間の所要時間」もコンバージョンの質を測る重要指標です。view → click の平均時間が長すぎる場合は決断材料の不足、click → purchase が長い場合は決済フローのフリクションが疑われます。
-- 直前イベントとの時間差を秒単位で取得 EXTRACT(EPOCH FROM (event_time - prev_event_time)) -- 60で割って分単位、ROUND で小数第1位 ROUND(AVG(...) / 60, 1)
timestamp - timestamp は interval 型を返し、EPOCH を抽出すると秒単位の数値になります。MySQL の TIMESTAMPDIFF(SECOND, t1, t2)、BigQuery の TIMESTAMP_DIFF(t2, t1, SECOND) と同じ役割を果たします。秒からの単位変換は /60 → 分、/3600 → 時間 です。user_events テーブルから、各ユーザーの連続イベント(view → click、click → purchase)の所要時間を計算し、遷移種別ごとの件数と平均分数を出力してください。出力列は transition, event_count, avg_minutes(小数第1位)、view → click、click → purchase の順で返してください。
| user_id | event_type | event_time |
|---|---|---|
| 1 | view | 2024-01-10 10:00 |
| 1 | click | 2024-01-10 10:10 |
| 1 | purchase | 2024-01-10 11:00 |
| 2 | view | 2024-01-11 09:00 |
| 2 | click | 2024-01-11 09:30 |
| 3 | view | 2024-01-11 14:00 |
| 3 | click | 2024-01-11 14:05 |
| 3 | purchase | 2024-01-11 14:35 |
| transition | event_count | avg_minutes |
|---|---|---|
| view -> click | 3 | 15.0 |
| click -> purchase | 2 | 40.0 |
再帰CTE — 日付ディメンションを生成し LEFT JOIN で DAU 時系列のゼロ埋めを行う
DAU(Daily Active Users)の時系列を可視化するときの最大の落とし穴がイベントのない日が結果から消えてしまう問題です。素直に GROUP BY date するとアクティビティがゼロの日は存在しない行になり、グラフが連続せず誤った印象を与えます。これを防ぐのが再帰CTE による日付ディメンションの生成です。
WITH RECURSIVE date_series(d) AS ( SELECT MIN(event_time::date) FROM user_events -- 起点(アンカー) UNION ALL SELECT d + 1 -- 再帰: 累積結果の各行から+1日した行を生成 FROM date_series WHERE d < (SELECT MAX(event_time::date) FROM user_events) )
user_events テーブルから、最初のイベント日から最後のイベント日まで全ての日付を出力し、各日付の DAU(アクティブユーザー数)を計算してください。イベントのない日は DAU=0 として出力します。出力列は event_date, dau、event_date 昇順で返してください。
| user_id | event_time |
|---|---|
| 1 | 2024-01-10 |
| 2 | 2024-01-10 |
| 1 | 2024-01-12 |
| 3 | 2024-01-12 |
| 1 | 2024-01-14 |
| event_date | dau |
|---|---|
| 2024-01-10 | 2 |
| 2024-01-11 | 0 |
| 2024-01-12 | 2 |
| 2024-01-13 | 0 |
| 2024-01-14 | 1 |
連続ログイン日数 — Gaps and Islands パターンで日付 − ROW_NUMBER 差分でグループ化し最長ストリークを算出
「連続ログイン日数」「連続購入日数」のように連続した塊(ストリーク)を検出する問題は、Gaps and Islands という古典的なパターンで解きます。各行に ROW_NUMBER() を採番し、日付 − 行番号 という不思議な差分を計算すると、連続した日付の塊では同じ値、ギャップが入ると別の値になるという性質を利用します。
-- 日付 - row_number(日) で「島」を識別する魔法のキー login_date - ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY login_date ) * INTERVAL '1 day' AS streak_group
user_logins テーブルから、各ユーザーの最長連続ログイン日数(longest_streak)を計算してください。出力列は user_id, longest_streak、user_id 昇順で返してください。
| user_id | login_date |
|---|---|
| 1 | 2024-01-01 |
| 1 | 2024-01-02 |
| 1 | 2024-01-03 |
| 1 | 2024-01-05 |
| 1 | 2024-01-06 |
| 2 | 2024-01-10 |
| 3 | 2024-01-15 |
| 3 | 2024-01-16 |
| 3 | 2024-01-17 |
| 3 | 2024-01-18 |
| user_id | longest_streak |
|---|---|
| 1 | 3 |
| 2 | 1 |
| 3 | 4 |