青の統計学-DS Playground-

current-state 分類:現在のプラン状態を intermediate に切り出す

Stage 4 — 第1章 | dbt入門カリキュラム 推定学習時間:40〜50分 | 難易度:★★★★☆


この章で学ぶこと

Stage 3 までで、activity mart と membership mart を学びました。 Membership mart が 「何人いるか」 を数えるのに対し、Monetization 次元は 「今その人はどの課金状態か」 を表します。

SaaS やサブスクリプション型プロダクトでは、会員数と課金状態は別の問いです。 会員数は terms 同意や登録日を軸に数えますが、課金状態は entitlement(権利)purchase(決済履歴) を組み合わせて定義します。 この定義を mart ごとにコピーすると、Premium セグメントの解釈が表ごとにずれます。

そこで dbt では intermediate モデル に current-state 分類を 1 箇所に固定し、下流 mart が共通の次元として参照するパターンが一般的です。

まず dbt の作法を整理し、続けて DS Playground の int_member_status_current を読み解きます。

この章を終えると、こんなことができるようになります:

  • premium / free / former_premium の定義をソース表付きで説明できる
  • anon_user_key が導入される位置づけを理解できる
  • current-state 分類の 歴史的限界(as-of 分析不可)を説明できる
  • 転換 mart との接続(signed_up_date, first_paid_date)を追える

current-state 分類とは何か

課金状態を表す列(例: member_status, customer_tier)には、大きく 2 種類の設計があります。

設計 意味 典型用途
current-state build 時点(または最新スナップショット)での状態 ダッシュボードの現状セグメント、配信対象の絞り込み
as-of / point-in-time 任意の過去日時点での状態 コホート分析、イベント当時の tier 再分類

dbt の intermediate で current-state を切り出すとき、入力はだいたい次の 3 系統に分かれます。

同意・登録 … 誰を「会員」とみなすか(DS Playground では terms 同意)。
entitlement … 今、有効なアクセス権があるか。
purchase / billing … 過去に課金したことがあるか。

Auth テーブル(ログイン情報)を analytics で読まないのは、アクセス制御 で学んだ境界と同じです。 課金状態は ビジネス上の権利と決済 から定義し、認証基盤とは分けます。

flowchart TD subgraph inputs["入力ソース(概念)"] CON["同意・登録"] ENT["entitlement(現在有効権利)"] PUR["purchase(決済履歴)"] end INT["intermediate
current-state 分類"] M1["転換 mart"] M2["activity 分割 mart"] M3["engagement mart"] CON --> INT ENT --> INT PUR --> INT INT --> M1 INT --> M2 INT --> M3

intermediate の出力 grain は 1 行 = 1 会員(母集団に含まれるユーザー) です。 下流 mart はこの表を JOIN して member_status 列を付与するだけに留め、CASE 文をコピーしません。


3 値 + unknown 分類の decision tree

current-state の典型パターンは、entitlement を優先し、なければ購入歴で分岐し、それもなければ free とする 3 値分類 です。

flowchart TD START["terms 同意済み user_id"] --> Q1{"active premium_member
entitlement あり?"} Q1 -->|Yes| PRE["premium"] Q1 -->|No| Q2{"過去に paid 相当の
premium_member 購入あり?"} Q2 -->|Yes| FP["former_premium"] Q2 -->|No| FR["free"] ACT["int_user_activity_events の user"] --> J["LEFT JOIN int_member_status_current"] J --> UNK["マッチなし → unknown
(status 分割 mart のみ)"]

SQL 上の CASE は概念として次のとおりです。

case
            when coalesce(has_active_premium_entitlement, 0) = 1 then 'premium'
            when paid_purchases.user_id is not null then 'former_premium'
            else 'free'
          end as member_status
          
状態 自然言語 典型ユースケース
premium 現在有効な Premium entitlement 有料機能利用モニタリング
former_premium 今は無効だが課金歴あり 復帰キャンペーン対象分析(内部)
free 課金歴なしの terms 会員 無料体験・転換ファネル
unknown terms ベースにいない activity 品質・境界の監視

unknown は intermediate 本体には出さず、activity 行に status を LEFT JOIN するときだけ現れます。 定義上マッチしなかった行を隠さず、利用者が前提を確認できるようにするためです(Mart 設計 の grain 原則)。


current-state の限界

current-state 分類の重要な caveat は、過去イベントを build 時点の tier で遡及しない ことです。

例えば、先月 free で学習し今月 premium 化したユーザーは、過去の activity 行を premium に再分類しません。 厳密な「イベント日 as-of Premium」が必要なら、entitlement history の intermediate や snapshot が別途必要です。

指標定義 で学んだ「スナップショット指標 vs フロー指標」の区別がここに当てはまります。 ダッシュボードで current-state の member_status を使うときは、「今の状態での分割」 であることを併記してください。

Monetization status(今どの tier か)と Monetization conversion(いつ signed up / first paid したか)は別 mart です。 混ぜないのは Mart 設計 の「1 mart = 1 business question」原則どおりです。


int_member_status_current

DS Playground では、中間モデル int_member_status_current が Monetization 次元の SSOT です。 terms 同意会員を母集団とし、entitlements と purchases から 3 値分類を行います。

入力ソース

ソース 役割
stg_supabase__consents terms 同意 — Member の入口(このモデルは terms 会員のみ)
stg_supabase__entitlements premium_member現在有効 アクセス
stg_supabase__premium_purchases 課金履歴(初回 paid_at など)
stg_supabase__user_learning_preferences primary_goal(セグメント用)

Auth は読みません。アクセス制御 と同じ境界です。

premium 判定の詳細

entitlements 側では、product_code が premium_member で、status が active、かつ有効期間内の行を集約します。

max(case
            when entitlement_status = 'active'
              and starts_at <= current_timestamp
              and (ends_at is null or ends_at > current_timestamp)
              then 1
            else 0
          end) as has_active_premium_entitlement
          

current_timestamp で有効性を判定するため、これは build 時点の current-state です。

paid 購入側は次の条件で「一度も課金した」とみなします。

where product_code = 'premium_member'
            and purchase_status in ('paid', 'partially_refunded')
            and paid_at is not null
          

partially_refunded を paid-like に含めるのは 実務上の「一度も課金した」 判定です。定義変更時は YAML caveats を更新します。

出力列の読み方

意味
user_id 内部 JOIN 用(engagement mart 以外では非公開方針)
anon_user_key md5(user_id::text) — 疑似匿名キー(後章)
signed_up_date terms 同意初日 → membership / conversion mart
first_paid_date 初回 paid 購入日
has_active_premium boolean
has_ever_paid boolean
lifetime_paid_amount_total 累計課金額(内部分析)
primary_goal 学習目的(demand 分析との接点)

下流 mart との関係

flowchart LR IMS["int_member_status_current"] --> FC["fct_membership_conversion_daily"] IMS --> AS["fct_active_users_daily_by_member_status"] IMS --> ENG["fct_user_engagement_current"] IMS --> DIST["fct_user_engagement_distribution_current"]

int_member_status_current を変更すると、Premium セグメントを使う すべての mart の数字が変わります。 だからこそ intermediate に CASE 文を集約し、mart 側は JOIN だけに留めます。


体系コラム:カリキュラム上の位置づけ

項目 内容
Stage / 章 Stage 4 — 第1章
今回の論点 3 値分類、entitlement vs purchase、current-state 限界
前章との接続 Incremental mart
次章への伏線 Engagement mart

まとめ

課金状態は 1 つの CASE 文より、ソースごとの責務(同意・権利・決済)を整理したうえで intermediate に組み立てます。 DS Playground の int_member_status_current は terms 同意会員を入口に、entitlement 優先 → 購入歴 → free の順で分類し、転換・activity 分割・engagement mart の共通次元になっています。

current-state である限界(as-of 不可)を理解したうえで使うことが、Monetization 分析の前提です。


関連教材


確認問題

問題 1

current-state の customer_tier 列について 正しい 説明はどれですか。

A. 任意の過去日のイベントを、その日時点の tier で自動再分類する
B. build 時点の状態であり、厳密な as-of 分析には snapshot 等が必要
C. OLTP の role 列と常に一致する
D. 公開ダッシュボードに raw user_id 付きで出してよい

正解: B


問題 2

entitlement(権利)テーブルと購入履歴テーブルを 両方 intermediate で読む主な理由はどれですか。

A. SQL を短くするため
B. 現在有効な権利と、過去購入の有無を組み合わせてステータスを定義するため
C. seed を自動生成するため
D. incremental を有効にするため

正解: B


問題 3

mart の member_status enum に unknown を残す 主な理由 はどれですか。

A. データ品質が悪いから必ず除外する
B. 定義上マッチしなかった行を隠さず、利用者が前提を確認できるようにするため
C. BI ツールの制約のため
D. staging が view だから

正解: B


用語メモ(この章)

用語 意味(この章での使い方)
current-state build 時点での最新状態。過去 as-of 不可
entitlement プロダクトへのアクセス権(有効期間付き)
former_premium 今は無効だが課金歴がある状態
intermediate 課金状態定義の SSOT を置く層
unknown terms 母集団にいない activity 行の status