SQL 行動分析 — アトリビューション・RFM分析の応用

応用行動分析FIRST / LAST_VALUEセッショナイゼーション階層CTE2軸RFMPostgreSQL対応全5問
1 / 5 · 学習中 0 / 5 問完了
QUESTION 6

アトリビューション分析 — FIRST_VALUE / LAST_VALUE でファースト&ラストタッチを同時取得する

FIRST_VALUELAST_VALUEROWS BETWEENアトリビューションマルチタッチ
前提知識

FIRST_VALUE / LAST_VALUE はパーティション内の最初・最後の値を返すウィンドウ関数です。ウィンドウフレーム(ROWS BETWEEN …)を明示しないと意図しない値が返るため注意が必要です。

FIRST_VALUE(channel) OVER (
  PARTITION BY user_id
  ORDER BY     touched_at
  ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING  -- パーティション全体を窓に
) AS first_channel
LAST_VALUE の落とし穴:ORDER BY を伴うウィンドウ関数のデフォルトフレームは RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW です。この既定では LAST_VALUE現在行の値を返してしまい、最終行の値になりません。必ず ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING を明示してください。
マーケティング・アトリビューション:ファーストタッチ(初回接触チャネル)は認知獲得への貢献、ラストタッチ(直前接触チャネル)はコンバージョン直前の後押しを意味します。両者が一致するか否かで「短期決断ユーザー」と「比較検討型ユーザー」を区別できます。
問題

touch_events テーブルから、各ユーザーのファーストタッチチャネル・ラストタッチチャネル・総接触回数・両者が一致しているかを取得してください。取得列は user_id, first_channel, last_channel, total_touches, is_same_channel、user_id 昇順で返してください。

使用テーブル
▸ touch_events(8行)
user_idchanneltouched_at
1google2024-01-05
1email2024-01-10
1direct2024-01-15
2facebook2024-02-01
2facebook2024-02-08
3google2024-02-10
3email2024-02-12
3google2024-02-20
期待出力
user_idfirst_channellast_channeltotal_touchesis_same_channel
1googledirect3FALSE
2facebookfacebook2TRUE
3googlegoogle3TRUE
QUESTION 7

セッショナイゼーション — LAG + 累積SUM OVER で30分間隔ごとにセッションIDを採番する

LAGSUM OVERCASE WHENセッショナイゼーションセッション分割
前提知識

セッショナイゼーションはWeb解析の最重要パターンの一つです。一定時間(典型的には30分)を超えて空いたアクセスを別セッションとして分割します。実装にはLAGで前行との時間差を測り、しきい値超過にフラグを立て、その累積和でセッションIDを採番します。

-- セッショナイゼーションの中核ロジック:累積SUMで連番化
new_session_flag        cumulative SUM (= session_id)
1   ← 最初の行             →  1
0   ← 30分以内              →  1
0                          →  1
1   ← 30分超 = 新セッション   →  2
0                          →  2
1                          →  3
EXTRACT(EPOCH FROM …) / 60:TIMESTAMP 同士の差は INTERVAL 型です。分単位で扱うには EPOCH(秒)に変換して 60 で割るのが定石。EXTRACT(EPOCH FROM (t2 - t1)) / 60 で「t1からt2までの分数」を取得できます。
問題

page_views テーブルから、各ユーザーの全ページビューに対し30分を超えて空いたら新セッションとしてセッションIDを採番してください。取得列は user_id, viewed_at, gap_minutes, session_id、user_id・viewed_at の昇順で返してください。

使用テーブル
▸ page_views(8行)
user_idviewed_at
12024-01-05 10:00
12024-01-05 10:15
12024-01-05 10:25
12024-01-05 11:30
12024-01-05 11:45
22024-01-05 14:00
22024-01-05 15:00
22024-01-05 15:10
期待出力
user_idviewed_atgap_minutessession_id
12024-01-05 10:00NULL1
12024-01-05 10:15151
12024-01-05 10:25101
12024-01-05 11:30652
12024-01-05 11:45152
22024-01-05 14:00NULL1
22024-01-05 15:00602
22024-01-05 15:10102
QUESTION 8

最長連続購入ストリーク — GAP-AND-ISLAND + ランキング + 継続中判定で実務レベルのKPI算出

GAP-AND-ISLANDROW_NUMBERMAX OVER最長ストリーク継続中フラグ
前提知識

基礎編で学んだ GAP-AND-ISLAND をさらに発展させ、各ユーザーの最長連続購入期間そのストリークが現在も継続中かを1クエリで算出します。GAP-AND-ISLAND で島(連続期間)を検出した後、ユーザーごとに最長島をランク付けし、最終購入日との一致を確認します。

-- 4段のCTEで進める設計
1) numbered  : ROW_NUMBER で連番付与
2) grouped   : date - (rn-1) で island キー生成
3) streaks   : MIN/MAX/COUNT で各 island を集約
4) ranked    : ROW_NUMBER で最長島を1位に、MAX(end) OVER で最新購入日を取得
5) 最終SELECT: rnk=1 行を抽出、is_active 判定
同点ストリークの tie-break:同じ最長日数のストリークが複数ある場合、より新しい streak_end のものを優先します(ORDER BY streak_days DESC, streak_end DESC)。実務上「最近の最長記録」のほうが施策判断に有用なためです。
問題

daily_orders テーブルから、各ユーザーの最長連続購入ストリーク(開始日・終了日・日数)と、そのストリークが現在も継続中かを算出してください。「継続中」とは「最長ストリークの終了日 = そのユーザーの最新購入日」であることを意味します。取得列は user_id, max_streak_days, streak_start, streak_end, is_active、user_id 昇順で返してください。

使用テーブル
▸ daily_orders(13行)
user_idorder_date
12024-01-01
12024-01-02
12024-01-03
12024-01-05
12024-01-06
22024-01-10
22024-01-11
22024-01-12
22024-01-13
22024-01-14
32024-01-20
32024-01-21
32024-01-25
期待出力
user_idmax_streak_daysstreak_startstreak_endis_active
132024-01-012024-01-03FALSE
252024-01-102024-01-14TRUE
322024-01-202024-01-21FALSE
QUESTION 9

招待ツリー階層分析 — WITH RECURSIVE で多段の招待関係を辿り招待深度と人数を集計する

WITH RECURSIVE階層クエリJOIN 自己参照招待ツリー階層深度
前提知識

基礎編では WITH RECURSIVE を「日付シーケンス生成」に使いました。応用編では本来の目的である階層構造(ツリー)の探索に挑みます。招待関係や組織図、コメントスレッド、ファイルシステムなど自己参照テーブルを辿る場面の定番テクニックです。

-- 階層再帰の構造(招待ツリーを上から辿る)
WITH RECURSIVE tree AS (
  -- アンカー:ルート(招待者なしのユーザー)から開始
  SELECT user_id, 0 AS depth
  FROM   users
  WHERE  invited_by IS NULL
  UNION ALL
  -- 再帰:前回の結果と自己参照JOIN、depth を +1
  SELECT u.user_id, t.depth + 1
  FROM   tree t
  JOIN   users u ON u.invited_by = t.user_id
)
累積結果 vs 今回追加された行:再帰CTE で FROM tree と書くと、前回の再帰イテレーションで追加された行のみを参照します(累積結果全体ではない点に注意)。これにより JOIN が「次の階層」だけを生成する設計になっています。
サイクル検出:招待ツリーに循環(A→B→A)があると無限ループになります。本問のデータには循環がない前提ですが、実務では WHERE NOT visited やパス配列追跡でサイクル検出するのが安全です。
問題

users テーブル(自己参照:invited_by 列がそのユーザーを招待した人を指す)から、各ユーザーの招待深度(depth:ルートから何段下か)と、そのユーザーが直接招待した人数を取得してください。取得列は user_id, name, depth, direct_invitees、depth・user_id 昇順で返してください。

使用テーブル
▸ users(7行・自己参照)
user_idnameinvited_by
1AliceNULL
2Bob1
3Carol1
4Dave2
5Eve2
6Frank4
7Grace3
期待出力
user_idnamedepthdirect_invitees
1Alice02
2Bob12
3Carol11
4Dave21
5Eve20
7Grace20
6Frank30
QUESTION 10

2軸RFMマトリクス — Recency × Monetary の3×3グリッドで実践的セグメントを構築する

NTILE複数CTE2軸セグメントRFMマトリクスセグメント命名
前提知識

基礎編では NTILE(4) で Monetary(購入額)の1軸セグメントを学びました。応用編では実務で本当に使われる R × M(Recency × Monetary)の2軸マトリクスに挑戦します。各軸で NTILE(3) を適用し、3×3=9 セルそれぞれにビジネス意味のあるラベルを付与します。

R↓ M→
M=1(低額)
M=2(中額)
M=3(高額)
R=3(最近)
新規/育成
優良見込
VIP
R=2(中間)
一般
標準
離脱注意
R=1(過去)
休眠
休眠
失注リスク
SELECT id_col,
       NTILE(3) OVER (ORDER BY col_a DESC) AS tile_a,  -- 降順で3分割(大きい値が 1)
       NTILE(3) OVER (ORDER BY col_b)      AS tile_b   -- 昇順で3分割(小さい値が 1)
FROM   table_name;
NTILE で Recency を計算する向き:「最近購入したユーザーが R=3(高スコア)」になるよう ORDER BY last_purchase_date DESC降順NTILE を適用し、得られたランクを 4 - r_rank で反転します。日付昇順だと古いユーザーが3になり意味が逆転するため要注意。
問題

customer_purchases テーブルから、各ユーザーの最終購入日(Recency)と合計購入額(Monetary)に NTILE(3) を適用し、3×3=9セルのマトリクスに分類してセグメントラベルを付与してください。取得列は user_id, last_purchase_date, total_amount, r_score, m_score, segment、r_score・m_score の降順で返してください。

使用テーブル
▸ customer_purchases(12行)
user_idpurchase_dateamount
12024-03-2515000
22024-03-208000
32024-03-282000
32024-03-302500
42024-02-1512000
52024-02-105000
52024-02-252000
62024-02-203000
72024-01-0520000
72024-01-2010000
82024-01-105000
92024-01-151500
期待出力
user_idlast_purchase_datetotal_amountr_scorem_scoresegment
12024-03-251500033VIP
22024-03-20800032優良見込
32024-03-30450031新規/育成
42024-02-151200023離脱注意
52024-02-25700022標準
62024-02-20300021一般
72024-01-203000013失注リスク
82024-01-10500012休眠
92024-01-15150011休眠