青の統計学-DS Playground-

派生列と命名規約

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


この章で学ぶこと

staging では、生のタイムスタンプ列に加えて cast(... as date) の日付列 を派生させることがよくあります。 これは SQL を 1 行短くするためではなく、「同じ日付の定義」を組織で共有する ための設計です。

dbt プロジェクトでは、列名も レイヤーごとのスコープ を持ちます。 staging ではソースに近い名前(attempted_date)、intermediate では横断的な名前(date_day)—— という使い分けが DS Playground でも採用されています。

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

  • 日付派生列を staging で作る 理由と限界 を説明できる
  • *_at / *_dateペア命名 を読み書きできる
  • semantics を隠すリネーム・COALESCE を アンチパターン として見分けられる
  • intermediate で 共通語彙 に揃える判断を説明できる

なぜ mart ではなく staging で日付を切るか

日次 KPI(DAU、会員数の日次推移など)は、ほぼ必ず 日付で GROUP BY します。 ここで mart ごとに cast(completed_at as date) を書くと、同じ「1 日」の境界がモデルごとに微妙にずれ、列名も event_date / day / dt と乱立します。

flowchart LR BAD["アンチパターン
mart ごとに日付定義"] M1["mart A
cast(a as date)
event_date"] M2["mart B
cast(b as date)
day / dt"] GOOD["推奨
staging で1つの定義"] STG["staging
captured_date"] MA["mart 1
captured_date を利用"] MB["mart 2
captured_date を利用"] BAD --> M1 BAD --> M2 BAD -.->|"定義を一箇所へ"| GOOD GOOD --> STG STG --> MA STG --> MB classDef bad fill:#fef2f2,stroke:#dc2626,color:#1f2937 classDef good fill:#f0fdf4,stroke:#16a34a,color:#1f2937 class BAD,M1,M2 bad class GOOD,STG,MA,MB good

staging で 1 回だけ 日付列を派生させ、下流は captured_dateattempted_date を信頼する——これが一般的な規約です。 データアナリティクス入門の日付集計 で学んだ「分母・分子の日付定義を揃える」と同じ発想を、列レベル で固定しています。


命名規約:*_at*_date

サフィックス 型の想定 用途
*_at timestamp(時刻付き) 順序付け、精緻な時系列
*_date date(日付のみ) 日次集計、カレンダー JOIN

DS Playground では staging 横断でこのペアが使われます。

-- stg_supabase__consents
          captured_at,
          cast(captured_at as date) as captured_date

          -- stg_supabase__problem_attempts
          attempted_at,
          cast(attempted_at as date) as attempted_date
          

名前に「何の日付か」を入れる

指標定義 で「売上」と一語で呼ばないのと同様、日付列も イベント名を冠 します。

良い例 悪い例 理由
captured_date date1 何の日付か不明
completed_date active_day 「アクティブ」の定義が別途必要
paid_date conversion_date 会計・マーケで意味が割れやすい

「BI で見やすい短い名前」は、staging では ソース semantics を上書き しがちです。 ビジネス向けのラベルは mart の description や BI の表示名に任せ、staging では ソースに近い名前 を維持するのが安全です。


semantics を隠す派生 — アンチパターン

過度な COALESCE

-- アンチパターン: 異なる意味の時刻を 1 列に潰す
          coalesce(completed_at, last_read_at, updated_at) as activity_at
          

「完了」「閲覧」「更新」が 1 列に混ざると、Metabase で「activity_at とは?」が再発します。 DS Playground では、イベント統合は intermediate の int_user_activity_eventsactivity_type 付きで UNION します。意味の混在は staging ではなく、型を揃えた統合層 で明示します。

意味の変わるリネーム

-- アンチパターン
          is_granted as is_member
          

is_granted特定 consent_type の付与 であり、「会員であること」全体ではありません。 terms 同意ベースの会員 proxy は別モデル(int_member_status_current)が担います。staging で語彙をすり替えると、下流が 誤った前提 で JOIN します。


ルールを明示する派生:normalized_version

すべての派生列が「日付 cast」だけではありません。 ビジネスルールを含む派生 も staging に置けますが、その場合は ルールを隠さない命名と YAML が必須です。

stg_supabase__skill_check_results では、スキルチェックの version 列に null や空文字がある場合、旧版 genege-v3 とみなす normalized_version 列を派生させています。

役割
version 生の値(null あり)—— 上書きしない
normalized_version 明示的 fallback ルール付きの派生列
# _staging__models.yml(抜粋)
          - name: normalized_version
            description: "空/nullのversionを旧版 `genege-v3` とみなして正規化したスキルチェックバージョン。"
            data_tests:
              - not_null
          

ポイントは 3 つです。

  1. 生列 version を残し、加工前の状態を追える
  2. 接頭辞 normalized_*加工列である ことを示す
  3. description に fallback ルールを書く(次章 の test とセット)

singular test assert_skill_check_version_supported は、新バージョン追加時の 変更検知 として機能します(Stage 2 第4章)。


intermediate での共通語彙:date_day

複数 staging を UNION する intermediate では、ソースごとに異なる日付列名を 横断的な名前 に揃えます。

int_user_activity_events の一部(概念):

select
            user_id,
            attempted_at as occurred_at,
            attempted_date as date_day,
            'practice_problem_saved' as activity_type
          from {{ ref('stg_supabase__problem_attempts') }}

          union all

          select
            user_id,
            completed_at as occurred_at,
            completed_date as date_day,
            'curriculum_page_completed' as activity_type
          from {{ ref('stg_supabase__user_curriculum_page_progress') }}
          where status = 'completed'
            and completed_at is not null
          
スコープ
occurred_at 統合後のイベント時刻(ソースごと元列名は異なる)
date_day 日次 grain 用の共通名(intermediate 以降)
activity_type 何のイベントか(enum)

staging では attempted_date のように ソース固有名 を保ち、intermediate で date_day昇格 する。 名前のスコープがレイヤーごとに変わる、というのが dbt 設計の読みどころです。

flowchart LR S1["staging
attempted_date"] S2["staging
completed_date"] INT["intermediate
date_day"] MART["mart
GROUP BY date_day"] S1 --> INT S2 --> INT INT --> MART

タイムゾーンと caveats

cast(timestamp as date) は、データベースセッションのタイムゾーン解釈に依存します。 厳密な JST 日次境界が必要なプロジェクトでは、AT TIME ZONE や macro で 明示的に 境界を固定します。

DS Playground の overview では、カレンダー spine 未導入など 運用上の TODO も docs に残されています。 良い dbt プロジェクトは、数字だけでなく 「この日付列をどう読んではいけないか」 も YAML / overview に書きます。


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

項目 内容
Stage / 章 Stage 2 — 第2章
今回の論点 派生列は 明示的命名 で semantics を守る
前章との接続 staging 正規化
次章への伏線 schema tests

まとめ

派生列は「SQL を短くするため」ではなく、同じ定義を組織で共有するため に存在します。 DS Playground では *_at / *_date のペア、intermediate の date_day、ルール付き normalized_* が、その実装例です。


関連教材


確認問題

問題 1

staging で cast(order_placed_at as date) as order_placed_date を作る 主な理由 として最も適切なのはどれですか。

A. timestamp 型を禁止しているから
B. 日次 mart など下流が共通の日付列定義を再利用できるようにするから
C. データウェアハウスが date 型しか返さないから
D. seed ファイルとの JOIN のためだけ

正解: B


問題 2

次のうち 命名として最も問題が大きい のはどれですか。

A. order_source(元 source 列)
B. is_shipped as is_paid
C. order_placed_date
D. normalized_status

正解: B

解説: 出荷済みと支払済みは別概念です。staging で語彙をすり替えると下流の前提が壊れます。


問題 3

複数 staging を UNION する intermediate で date_day という列名を使う 主な理由 はどれですか。

A. staging で必ず date_day という名前を付ける規則だから
B. ソースごとに異なる日付列名を 横断的な日次 grain 名 に揃えるため
C. BI ツールのパラメータ名だから
D. seed CSV の列名だから

正解: B


用語メモ(この章)

用語 意味(この章での使い方)
派生列 既存列から計算した列
semantics 列が「何を意味するか」
*_at / *_date 時刻付き / 日付のみのペア命名
date_day intermediate 以降の共通日次列名
normalized_* 明示的ルール付きの派生列