マクロ:共通ロジックを DRY に保つ
Stage 5 — 第1章 | dbt入門カリキュラム 推定学習時間:30〜40分 | 難易度:★★★☆☆
この章で学ぶこと
転換率・構成比・シェア列は mart 全体に散在します。
同じ CASE WHEN denominator = 0 をコピペすると、定義のドリフト と NULL/0 の不一致 が起きます。
まず dbt マクロで共通式を再利用する作法を整理し、続けて DS Playground の safe_ratio マクロ を読み解きます。
この章を終えると、こんなことができるようになります:
macros/safe_ratio.sqlを読んで展開結果を想像できる- マクロを使う mart 例を 3 つ以上挙げられる
- NULL と 0% の BI 上の違いを説明できる
- マクロ vs セマンティックレイヤーの役割分担を理解できる
dbt マクロとは何か
dbt マクロは、Jinja テンプレートで SQL 断片を関数化 する仕組みです。 build 時に展開され、warehouse へ送られるのは通常の SQL です。
| アプローチ | 問題 |
|---|---|
| mart ごとに CASE 文 | 修正漏れ、レビュー負荷 |
| BI 側で割り算 | ツールごとに定義がズレる |
| 共通マクロ | SQL 生成時に同じ式を注入 |
指標定義 では、CVR などの 分子分母 を固定することが重要でした。 dbt では、その「割り算の安全装置」を 1 マクロ に集約するのが典型パターンです。
マクロは魔法ではなく、組織が合意した小さな式の再利用 です。 過度な抽象化(1 行ヘルパーの乱立)は避け、複数 mart で繰り返すパターンだけ をマクロ化します。
比率計算とゼロ除算
mart で率を出すとき、分母が 0 または NULL の行をどう扱うかは 組織の契約 です。
| 条件 | 一般的な扱い |
|---|---|
| 分母 NULL | 率は定義されない → NULL |
| 分母 0 | ゼロ除算を避け NULL(または 0% と明示的に区別) |
| 分子 NULL | SQL 通常どおり NULL 伝播 |
BI では NULL(率が定義されない) と 0%(分子 0・分母あり) を混同しないことが重要です。 ダッシュボード説明文に「NULL = N/A」と書くのが運用上のベストプラクティスです。
NULL/0 か?"} OUT["numeric 除算"] NA["NULL"] NUM --> CHECK DEN --> CHECK CHECK -->|No| OUT CHECK -->|Yes| NA
マクロとセマンティックレイヤーの役割分担
セマンティックレイヤー の L2(SSOT マート)内で比率定義を固定する実装例が、dbt マクロです。
| レイヤ | 役割 |
|---|---|
| intermediate / mart | 何を数えるか(grain・分子分母の意味) |
| マクロ | どう割り算するか(ゼロ除算・型キャストの統一) |
| BI セマンティックレイヤ | 表示名・集計方法(mart 定義を上書きしない) |
Metabase 側で独自の割り算を増やすと、mart の SSOT が崩れます。 比率の 計算ルール は SQL(マクロ)側、ビジネス意味 は YAML と docs 側——という分担が基本です。
safe_ratio マクロ
DS Playground では safe_ratio が比率列の SSOT です。
{% macro safe_ratio(numerator, denominator) -%}
case
when {{ denominator }} is not null and {{ denominator }} != 0
then ({{ numerator }})::numeric / {{ denominator }}
end
{%- endmacro %}
挙動
| 条件 | 結果 |
|---|---|
| 分母 NULL | NULL |
| 分母 0 | NULL |
| それ以外 | numeric 除算 |
分子 NULL は SQL 通常どおり NULL 伝播します。
使用例(mart 内)
有料転換 — fct_membership_conversion_daily(Membership mart で学んだ 2 率):
{{ safe_ratio('total_paid_members', 'total_terms_members') }} as paid_member_conversion_rate,
{{ safe_ratio('new_paid_members', 'new_terms_members') }} as same_day_paid_conversion_rate
active user share — fct_active_users_daily_by_member_status:
{{ safe_ratio('active_users', 'total_active_users') }} as active_user_share
survey / engagement 分布:
{{ safe_ratio('goal_counts.users', 'totals.total_users') }} as user_share
fct_learning_goal_share, fct_member_survey_option_share, fct_user_engagement_distribution_current などで同型です。
マクロを使わない例
fct_problem_quality の current_accuracy_rate はインライン CASE です:
case
when coalesce(attempts.attempted_users, 0) > 0
then attempts.correct_users::numeric / attempts.attempted_users
end
| 観点 | 説明 |
|---|---|
| 分母 | 常に attempted_users — マクロ化の重複メリットは小 |
| outlier 条件 | 同じ行で < 0.10 判定 — 可読性のため近接配置 |
| 方針 | 共有パターンだけマクロ化。過度な抽象化は避ける |
Content quality mart と同様、1 行ヘルパーの乱立はしません。
マクロ変更時のチェックリスト
- 依存 mart 一覧を
dbt ls --select safe_ratio+で確認 - CI
analytics_ciで build + test - 率の列が 意味変更 なら YAML caveats と Metabase 凡例を更新
- 分析前チェックリスト でステークホルダー通知
体系コラム:カリキュラム上の位置づけ
| 項目 | 内容 |
|---|---|
| Stage / 章 | Stage 5 — 第1章 |
| 今回の論点 | DRY、ゼロ除算、NULL セマンティクス、マクロの適用範囲 |
| 前章との接続 | Content quality mart |
| 次章への伏線 | Snapshots と exposures |
まとめ
dbt マクロは、複数 mart に散在する 小さな式の SSOT です。
DS Playground では safe_ratio が conversion / share 系のゼロ除算ルールを統一し、BI 上の NULL と 0% の区別を守ります。
共有パターンだけマクロ化し、行内の特殊ロジックはインラインのまま残す——このバランスが、再利用と可読性の両立です。
関連教材
- 前章: Content quality mart
- 次章: Snapshots と exposures
- 関連(Analytics): 率と KPI
確認問題
問題 1
safe_ratio(5, 0) の結果はどれですか。
A. 0
B. NULL
C. 5
D. エラーで build 失敗
正解: B
問題 2
累計 conversion rate の計算に safe_ratio マクロを使う主な理由はどれですか。
A. user_id をハッシュ化するため
B. 累計列のゼロ除算を統一ルールで防ぐため
C. incremental を有効にするため
D. seed を読み込むため
正解: B
問題 3
BI で NULL と 0% を区別する理由として最も適切なのはどれですか。
A. グラフの色が変わるから
B. 「率が定義されない」と「率が 0」を混同しないため
C. SQL が短くなるから
D. OLTP の制約のため
正解: B
用語メモ(この章)
| 用語 | 意味(この章での使い方) |
|---|---|
| マクロ | build 時に SQL へ展開される Jinja テンプレート関数 |
safe_ratio |
ゼロ除算を避ける比率計算マクロ(DS Playground SSOT) |
| DRY | 同じ式をコピペせず 1 か所に集約する原則 |
| NULL vs 0% | 分母なし(未定義)と分子 0(率 0)の区別 |
| SSOT | 組織が合意した唯一の参照定義 |