SQL KPI分析 — MoM成長率・NTILE・ファネルCVRの応用

応用MoM成長率(LAG)NTILEセグメントファネル分析コホート分析再帰CTE(日付スパイン)PostgreSQL対応全5問
1 / 5 · 学習中 0 / 5 問完了
QUESTION 6

MoM成長率 — LAG() で前月の値を引き寄せ、前月比・前月差を計算する

LAGウィンドウ関数MoM/前月比NULLIF
前提知識

月次売上やMAUの「前月比成長率(MoM, Month-over-Month)」は経営報告の必須KPIです。これは同じ行に「今月の値」と「先月の値」を並べることで初めて計算できます。LAG() は、ORDER BY で並べた中でN行前の値を現在行へ引き寄せるウィンドウ関数で、まさにこの用途の定番です。

LAG(revenue)     OVER (ORDER BY month)   -- 1つ前の行の revenue(既定オフセット=1)
LAG(revenue, 12) OVER (ORDER BY month)   -- 12行前=前年同月(YoY)も同じ文法で
LEAD(revenue)    OVER (ORDER BY month)   -- LEAD は逆に「次の行」を引き寄せる
成長率の割り算は NULLIF で守る:成長率 = (今月 − 先月) / 先月。先月が 0 だとゼロ除算エラーになります。NULLIF(prev, 0) で分母が0のとき NULL に化けさせ、エラーを回避するのが実務の定石です。先頭月は先月が存在せず LAG が NULL を返すため、成長率も自然に NULL になります。
問題

monthly_revenue から、各月の売上・前月売上・前月差(mom_diff)・前月比成長率(mom_pct, %)を計算してください。出力列は month, revenue, prev_revenue, mom_diff, mom_pct、month 昇順、成長率は小数第2位まで丸めてください。

使用テーブル
► monthly_revenue(6行)
monthrevenue
2024-011000
2024-021200
2024-031100
2024-041500
2024-051500
2024-061800
期待出力
monthrevenueprev_revenuemom_diffmom_pct
2024-011000NULLNULLNULL
2024-021200100020020.00
2024-0311001200-100-8.33
2024-041500110040036.36
2024-051500150000.00
2024-061800150030020.00
QUESTION 7

顧客セグメント — NTILE() で支出を四分位に等分割しランク帯を作る

NTILEPARTITION/ORDERセグメント四分位CASE
前提知識

「上位25%の優良顧客」「支出の四分位(クォータイル)でランク帯を切る」——こうした等量分割によるセグメンテーションは NTILE(n) の出番です。NTILE は ORDER BY で並べた行をできるだけ均等な n 個のバケツに分け、各行へバケツ番号(1〜n)を振ります。

NTILE(4) OVER (ORDER BY spend DESC)  -- 支出降順で4等分。1=上位25%, 4=下位25%
-- 行数が割り切れないとき、余りは「前のバケツ」から1行ずつ多く配られる
--   例: 9行を4分割 → 3,2,2,2(先頭バケツが1行多い)
RANK との決定的な違い:RANK/DENSE_RANK は「値」が同じかどうかで順位を決めます。NTILE は「行数」をできるだけ均等にすることを優先し、値の大小差は問いません。だから値が大きく離れた2人が同じ帯に入ったり、僅差の2人がバケツ境界で分断されることもあります。
問題

customer_spend から、支出降順で顧客を4分割(四分位)し、帯ごとに VIP / Gold / Silver / Bronze のラベルを付与してください。出力列は user_id, spend, quartile, segment、spend 降順で返してください。

使用テーブル
► customer_spend(9行)
user_idspend
U1100
U2250
U3400
U4550
U5700
U6900
U71200
U81500
U91800
期待出力
user_idspendquartilesegment
U918001VIP
U815001VIP
U712001VIP
U69002Gold
U57002Gold
U45503Silver
U34003Silver
U22504Bronze
U11004Bronze
QUESTION 8

ファネル分析 — COUNT(DISTINCT)+FIRST_VALUE/LAG でステップ別CVRを出す

COUNT(DISTINCT)FIRST_VALUEファネル/CVRLAGCASE順序付け
前提知識

「閲覧 → カート → 購入」と進むユーザーがどこで脱落するか——ファネル分析はコンバージョン改善の出発点です。各ステップの到達ユーザー数を数え、ステップ間の転換率(CVR)を求めます。CVR には2つの定義があり、両方を1クエリで並べると示唆が深まります。

-- ① 全体CVR  : 入口(最初のステップ)を分母にした通過率
FIRST_VALUE(users) OVER (ORDER BY step_no)   -- 並びの先頭値=入口の人数で固定
-- ② ステップCVR: 直前のステップを分母にした転換率
LAG(users) OVER (ORDER BY step_no)           -- 1つ前のステップの人数
ステップに順序を持たせる工夫:'view'/'cart'/'purchase' は文字列なので、そのまま ORDER BY するとアルファベット順になってしまいます。CASE step WHEN 'view' THEN 1 ... で並び順を表す step_no を明示的に与えるのがポイント。ファネルは順序が命です。
問題

events(行動ログ)から、各ステップの到達ユーザー数(重複排除)と、全体CVR・ステップCVRを求めてください。出力列は step, users, overall_cvr, step_cvr、ファネル順(view→cart→purchase)で返し、CVRは%・小数第2位まで。

使用テーブル
► events(12行)
user_idstep
U1view
U1cart
U1purchase
U2view
U2cart
U3view
U4view
U4cart
U4purchase
U5view
U6view
U6cart
期待出力
stepusersoverall_cvrstep_cvr
view6100.00NULL
cart466.6766.67
purchase233.3350.00
QUESTION 9

コホート・リテンション — 経過月数で条件付き集計し残存率マトリクスを作る

コホートFILTER集計リテンションピボットNULLIF
前提知識

コホート分析は「いつ獲得したか(登録月)」でユーザーを束ね、その後の経過月ごとの残存率(リテンション)を追う手法です。獲得施策やプロダクト改善の効果を、世代(コホート)横断で比較できます。鍵は2つ——経過月数(month_offset)の算出と、それを列に展開する条件付き集計(ピボット)です。

-- 経過月数 = 活動月 − 登録月(月単位の差)
(EXTRACT(YEAR FROM (active_month || '-01')::date) - EXTRACT(YEAR FROM (cohort_month || '-01')::date)) * 12
  + (EXTRACT(MONTH FROM (active_month || '-01')::date) - EXTRACT(MONTH FROM (cohort_month || '-01')::date))

-- ピボット:経過月ごとの在籍人数を横に並べる(重複排除)
COUNT(DISTINCT user_id) FILTER (WHERE month_offset = 1)   -- 1ヶ月後の在籍数
リテンション率 = 経過Nヶ月の在籍 ÷ コホート初月(m0):各コホートの母数は登録月(offset=0)の人数。そこに対する各経過月の在籍割合がリテンションです。NULLIF(m0, 0) でゼロ除算を防ぎます。チャーン率(基礎編)はこの裏返し(1 − リテンション)です。
問題

activity(登録月コホート × 活動月)から、コホート別の初月人数と1ヶ月後・2ヶ月後リテンション率を出してください。出力列は cohort_month, cohort_size, ret_m1, ret_m2、cohort_month 昇順、率は%・小数第1位まで。

使用テーブル
► activity(11行)
user_idcohort_monthactive_month
U12024-012024-01
U12024-012024-02
U12024-012024-03
U22024-012024-01
U22024-012024-02
U32024-012024-01
U42024-022024-02
U42024-022024-03
U42024-022024-04
U52024-022024-02
U52024-022024-03
期待出力
cohort_monthcohort_sizeret_m1ret_m2
2024-01366.733.3
2024-022100.050.0
QUESTION 10

再帰CTE — 日付スパインを生成し、欠損日を0で埋めた連続時系列を作る

WITH RECURSIVEUNION ALL日付スパインLEFT JOINCOALESCE
前提知識

日次の売上やDAUは、活動が無い日にそもそも行が存在しないことがよくあります。このまま折れ線や移動平均(基礎編)にかけると、欠損日が詰められてグラフも計算もズレます。解決策は連続した日付の骨組み(日付スパイン)を自前で生成し、そこへ実績を左結合して欠損日を0で補完することです。骨組み生成の王道が再帰CTEです。

WITH RECURSIVE calendar AS (
  SELECT DATE '2023-08-01' AS d        -- ① アンカー:起点の1行(非再帰項)
  UNION ALL                              -- ② 結果を縦に積み増す
  SELECT d + 1 FROM calendar          -- ③ 再帰項:自分自身を参照し+1日
  WHERE d < DATE '2023-08-07'           -- ④ 終了条件:終点に達したら停止
)
再帰CTEは「現在の結果 → 1行追加 → また自分を参照」の繰り返し:アンカーが起点を1行作り、再帰項が直前に生成された行を入力に次の行を作ります。WHERE の終了条件が無い/緩いと無限ループになり、DBの再帰上限でエラー停止します。可視化でこの「積み上がり」を1ステップずつ追ってみましょう。
問題

飛び飛びの daily_sales を、2024-03-01〜03-05 の全5日が必ず並び、活動の無い日は売上0となる連続時系列に整えてください。出力列は event_date, sales、日付昇順。再帰CTEで日付スパインを作り、LEFT JOIN + COALESCE で補完します。

使用テーブル
► daily_sales(3行・飛び飛び)
event_datesales
2024-03-01100
2024-03-03150
2024-03-0480
期待出力
event_datesales
2024-03-01100
2024-03-020
2024-03-03150
2024-03-0480
2024-03-050