seeds:参照マスタを repo で持つ
Stage 2 — 第5章 | dbt入門カリキュラム 推定学習時間:35〜45分 | 難易度:★★★☆☆
この章で学ぶこと
dbt の seeds は、リポジトリ内の CSV など 静的マスタ をデータウェアハウスに load する機能です。
dbt seed で warehouse 上のテーブルになり、他モデルから ref('seed_name') で参照できます。
source が アプリ DB の動的データ なのに対し、seed は Git 管理の参照データ です。 「warehouse に載せたいが、アプリ DB には存在しないマスタ」——カタログ、為替レートのスナップショット、組織コード表など——を repo 側で正本管理するパターンです。
DS Playground では practice_problem_catalog seed が、練習問題 コンテンツの正本(分母) として fct_problem_quality などの marts を支えます。
まず seed の役割と設計判断を整理し、続けて DS Playground の practice_problem_catalog を読み解きます。
この章を終えると、こんなことができるようになります:
practice_problem_catalogの 列意味と grain を説明できる- 正答率を「回答行だけ」ではなく カタログ対比 で考えられる
- seed と Supabase
problem_attemptsの 役割分担 を説明できる - seed 関連 singular test が 分母を守る 理由を理解できる
なぜ seed が必要か
分析で「割合」を語るとき、分母(全体集合) が不明だと指標は意味を失います。 ユーザー行動表だけを見ると、触られた問題だけ が見え、未出題・未回答の問題は分母から漏れます。
| データ源 | 持っているもの | 持っていないもの |
|---|---|---|
Supabase problem_attempts |
ユーザーが 実際に触った 問題の最新状態 | 未出題・未回答問題 |
practice/ コンテンツ(Git) |
公開問題の 完全一覧 | ユーザー行動 |
seed practice_problem_catalog |
上記 JSON から生成した 分析用スナップショット | リアルタイム更新(再生成が必要) |
コンテンツ品質の問い「カテゴリ X の問題のうち、正答率が低いのはどれか」には 分母(全問題) が必要です。 回答のあった問題だけで割ると、人気問題だけが見える バイアスがかかります。
データマネジメント SSOT の考え方で、コンテンツ定義の正本は Git 側、行動データは Supabase 側 と分かれています。 seed は、その Git 正本を分析層へ運ぶ橋 です。
コンテンツ SSOT"] SEED["practice_problem_catalog
seed CSV"] WH["warehouse テーブル"] MART["fct_problem_quality"] GIT -->|"生成・同期"| SEED SEED -->|"dbt seed"| WH WH --> MART
seed と source の違い
Stage 1 で学んだ source() との対比です。
| source | seed | |
|---|---|---|
| 正本 | Supabase アプリ DB | Git CSV |
| 更新 | アプリ運用 | コンテンツ PR + 再生成 |
| 典型 | ユーザー行動 | 問題マスタ |
| 宣言 | _supabase__sources.yml |
_seeds.yml |
| 参照 | source('supabase', 'table') |
ref('seed_name') |
source は 外部システムの鏡、seed は repo 内の参照データ——役割が異なります。 混同しやすいのは「seed も warehouse に載るから source と同じでは?」という点ですが、正本の所在 と 更新トリガー が違います。
practice_problem_catalog
seed ファイルの中身
seeds/practice_problem_catalog.csv の先頭:
category_slug,section_slug,problem_id,problem_title,problem_level,problem_type,category_name
g-certification,ai-history-and-concepts,ai-history-and-concepts-001,人工知能の定義の捉え方,1,multiple_choice,AIの歴史と概念
g-certification,ai-history-and-concepts,ai-history-and-concepts-002,AI効果,1,multiple_choice,AIの歴史と概念
| 列 | 意味 |
|---|---|
category_slug |
練習カテゴリ(URL スラッグ) |
section_slug |
セクション |
problem_id |
セクション内問題 ID |
problem_title |
表示タイトル(mart のラベル) |
problem_level |
著者定義難易度 |
problem_type |
問題形式(例: multiple_choice) |
category_name |
人間可読カテゴリ名 |
grain: category_slug + section_slug + problem_id で 1 問題。
seeds YAML — 列契約
第3章 と同様、seed にも schema test を付けられます。
# seeds/_seeds.yml(抜粋)
seeds:
- name: practice_problem_catalog
description: >
Static practice problem catalog generated from dsplayground/practice/**/problems.json.
Used as the denominator for content quality and coverage metrics.
columns:
- name: category_slug
data_tests:
- not_null
- name: section_slug
data_tests:
- not_null
- name: problem_id
data_tests:
- not_null
- name: problem_level
description: "Author-defined difficulty level from the problem JSON."
| ポイント | 説明 |
|---|---|
| description | 生成元パスを明示(コンテンツ repo との接続) |
| not_null | 結合キーの欠損を seed 段階で禁止 |
| singular と併用 | grain 一意は assert_practice_problem_catalog_unique |
marts での分母としての JOIN
fct_problem_quality の考え方(概念 SQL):
-- 概念: カタログ LEFT JOIN 最新回答状態
select
cat.category_slug,
cat.section_slug,
cat.problem_id,
cat.problem_title,
cat.problem_level,
pa.attempt_state,
pa.attempted_at
from {{ ref('practice_problem_catalog') }} as cat
left join {{ ref('stg_supabase__problem_attempts') }} as pa
on cat.category_slug = pa.category_slug
and cat.section_slug = pa.section_slug
and cat.problem_id = pa.problem_id
分母側(seed)を LEFT の左 に置くのがポイントです。 全公開問題を保持し、回答の有無・正誤を 任意の属性 として付与します。
seed・分母] STG[stg_supabase__problem_attempts
最新回答] MART[fct_problem_quality] T1[assert_practice_problem_catalog_unique] T2[assert_problem_attempts_catalog_covered] SEED --> MART STG --> MART SEED --> T1 SEED --> T2 STG --> T2
| 出力解釈 | 意味 |
|---|---|
| JOIN 成功 + correct | その問題に最新正解状態あり |
| JOIN 成功 + incorrect | 最新状態が不正解 |
| LEFT JOIN で NULL | 未回答 / 未保存(分母には残る) |
カテゴリ集約は fct_problem_category_quality が同 seed を分母に使います。
データマート設計 で学んだ「grain を先に決める」が、seed JOIN でもそのまま効きます。
更新運用 — コンテンツ変更と seed
seed は 自動でアプリと同期しない 点に注意します。
コンテンツを変えたら、seed 再生成 → dbt seed / dbt build が必要です。
| イベント | 必要アクション |
|---|---|
| 新問題追加 | problems.json 更新 → seed 再生成 → dbt seed/build |
| slug リネーム | seed + singular 被覆テストが CI で検知 |
| 問題削除 | seed から消え、attempts に残れば被覆テストが失敗しうる |
dsplayground/practice/**/problems.json
│(生成スクリプト・手動同期)
▼
seeds/practice_problem_catalog.csv
│ dbt seed / build
▼
warehouse 上の practice_problem_catalog テーブル
パイプライン全体を見ると、コンテンツ(Git)→ seed(repo)→ warehouse → mart(BI) という流れが一本つながります。 seed が古いままだと、mart は 存在しない問題を分母に含めたり、新問題を見落としたり します。
テストによる分母保護
第4章 の singular test が seed を含む横断断言として機能します。
| テスト | 守るもの |
|---|---|
assert_practice_problem_catalog_unique |
seed 側 grain 重複 → 分母膨張防止 |
assert_problem_attempts_catalog_covered |
attempts 側が未知 problem を参照しない |
| seed 列 not_null | 結合キー欠損防止 |
重複が 1 件あると、同じ問題が 2 回カウント され、カテゴリ品質ランキングが歪みます。 schema test(seed 列の not_null)と singular test(grain 一意・被覆)が 分母の正しさ を二重に守ります。
Stage 2 の位置づけ
Stage 2 では、staging から seed まで Transform の前半 を通しました。
staging・派生列"] TEST["第3–4章
schema / singular tests"] SEED["第5章
seeds"] NEXT["Stage 3 以降
intermediate / marts"] STG --> TEST --> SEED --> NEXT
- staging … ソースを薄く整える
- 派生列 … 日付・正規化ルールを明示
- schema / singular tests … 契約を CI で固定
- seeds … repo 正本を warehouse へ
この土台の上に、intermediate で KPI 定義を固定し、marts で BI 向けに畳む——という流れが Stage 3 以降です。
体系コラム:カリキュラム上の位置づけ
| 項目 | 内容 |
|---|---|
| Stage / 章 | Stage 2 — 第5章(Stage 2 完結章) |
| 今回の論点 | 分母 を warehouse 内に持ち込む設計 |
| 前章との接続 | singular tests |
| 次章への伏線 | intermediate / marts トラック — fct_problem_quality 詳読 |
まとめ
seed は「小さなマスタ CSV」ではなく、コンテンツ SSOT を分析層へ運ぶ橋 です。
practice_problem_catalog があるから、DS Playground は「触られた問題だけ」ではなく 公開問題全体に対する品質 を語れます。
| 項目 | practice_problem_catalog |
|---|---|
| 役割 | 練習問題の静的カタログ(分母) |
| grain | category + section + problem_id |
| 生成元 | practice/**/problems.json |
| 品質 | not_null + unique singular |
| 活用 | fct_problem_quality / category_quality |
関連教材
- 前章: singular tests
- Stage 1 起点: なぜ dbt Core か
- 関連(SSOT): ER・SSOT・マスターデータ
確認問題
問題 1
dbt の seed の 主な用途 として最も適切なのはどれですか。
A. OLTP テーブルのリアルタイム同期
B. リポジトリ内 CSV など 静的参照マスタ を warehouse に load する
C. BI ツールのログイン認証
D. 外部 API からの Extract
正解: B
問題 2
割合指標の分母を seed マスタから LEFT JOIN で作る 最も重要な理由 はどれですか。
A. CSV の方が SQL より速い
B. 行動データに現れない ID も分母に含め、母集団全体に対する率を計算できる
C. seed は view にできない
D. source() では JOIN できない
正解: B
問題 3
seed の grain に対する GROUP BY ... HAVING count(*) > 1 型 singular test が検知する問題として 最も近い のはどれですか。
A. 同一キーが seed 内で重複し、分母が水増しされる
B. ユーザーが退会した
C. 数値列が 101 を超えた
D. enum 列に NULL がある
正解: A
用語メモ(この章)
| 用語 | 意味(この章での使い方) |
|---|---|
| seed | リポジトリ CSV を load する dbt 機能 |
| 分母 | 割合計算の全体集合(全公開問題) |
| catalog | 問題 ID・タイトル等の一覧 |
| LEFT JOIN | 分母全件 + 任意の回答状態 |
| 被覆 | attempts が catalog を参照できているか |