dbtのSemantic Layerでは、semantic_model内にmeasures(集計対象)を定義し、後段のmetricsやBIツールから一貫した集計を提供します。
本稿は公式ドキュメントに基づく安定概念(name、agg、expr、代表的な集計種別など)に絞り、バージョン差異の影響が小さい設計原則にフォーカスします。
measureは、特定の列(または式)に対して定めた集計規則のことです。semantic_modelが参照する基礎モデルの行に対し、sum・avg・min・max・count・count_distinctなどの集計を定義します。metricsはしばしばこれらのmeasureを組み合わせて利用しますが、measure自体は“何をどう集計するか”を表す最小単位です。
定義はYAMLで行い、一般的にname(一意な名称)、agg(集計種別)、expr(対象列や式)を指定します。countのように行数を数える場合はexprを省略でき、sumやavgでは対象列をexprで明示します。これらはdbtの公式仕様で長く安定している概念です。
よくある失敗は、粒度が不安定な基礎モデルにmeasureを置くことです。1レコード=1ファクト(例: 1注文、1明細、1日次スナップショット)を満たすモデルにmeasureを定義し、重複結合による二重計上を防ぎます。
NULLや重複の扱いも試験・実務で問われがちです。SUMはNULLを無視しますが、AVGは分母がNULLを除いた件数になる点を理解しておきます。COUNTはCOUNT(*)とCOUNT(col)で意味が違い、COUNT(col)はNULLを無視します。COUNT DISTINCTは非加法的であるため、上位粒度にロールアップすると値が一致しないことがあります。
measureが利用される流れ(概念)
集計タイプごとの加法性とNULLの扱いは、試験でも実務でも最重要ポイントです。下表で振る舞いを押さえ、ユースケースに適切なmeasureを選びます。
特に平均と重複排除(COUNT DISTINCT)は非加法で、上位粒度へのロールアップや他ディメンションとの同時集計時に直感と異なる結果になりがちです。
| 集計タイプ | 加法性(時間/ディメンション) | NULLの扱い | 典型的な落とし穴 |
|---|---|---|---|
| sum | 加法的(時間・カテゴリともに安全) | NULLは無視(多くのSQLで集計から除外) | 小数の丸め誤差、通貨は桁管理・通貨単位の統一が必要 |
| count | 加法的(重複しない集合に限る) | COUNT(*)は全行、COUNT(col)はNULL除外 | JOINで重複行が増えると水増し。COUNT(col)のつもりでCOUNT(*)を使う誤り |
| count_distinct | 非加法(集合の和で重複が潰れる) | NULLは多くの方言でカウント対象外 | 日別ユニークを合計しても月次ユニークに一致しない |
| avg | 非加法(再集計で値が変わる) | NULLは分母から除外(AVG(col)) | 上位粒度での再平均は誤差。分子/分母measureから算出を検討 |
| min/max | 非加法(選択関数) | NULLは無視されることが多い | 複数集合の比較では元の分布を反映しない |
measureは“どの粒度で足し合わせられるか”に直結します。semantic_modelが指す基礎モデルは、明確なエンティティ(例: order_id)や時間ディメンション(例: order_date day)を持ち、1行=1イベントまたは1スナップショットであることが理想です。
エンティティには主キー(primary)と外部キー(foreign)を適切に設定し、ディメンションにはtimeやcategoricalを割り振ります。こうすることで、count_distinctのような非加法measureでも、どの単位で解釈されるかを明確化できます。
以下は、注文ファクトテーブルを参照するsemantic_modelに対して、典型的なmeasureを定義した例です。sum、count、count_distinctの3種を示しています。avgなどの非加法集計は、必要に応じて分子(sum)と分母(count)を別measureで持つのが安全です。
このレベルの定義は安定した仕様に基づくため、dbtのバージョン差異の影響を受けにくく、試験でも強く問われます。
semantic_model内でのmeasure定義(YAML)
semantic_models:
- name: orders
model: ref('fct_orders')
entities:
- name: order_id
type: primary
- name: customer_id
type: foreign
dimensions:
- name: order_date
type: time
type_params:
time_granularity: day
- name: status
type: categorical
measures:
- name: revenue
agg: sum
expr: total_amount
- name: order_count
agg: count
- name: customer_count
agg: count_distinct
expr: customer_id
measureの品質は基礎モデルの品質に依存します。主キーの一意性、外部キーの被参照整合性、金額列の型・スケール、時間ディメンションの欠損などをdbt testで確かめます。また、簡単な検証SQLでサンプル期間の期待値と突合し、JOINで水増しがないかを確認します。
試験対策としては、加法性、NULLの扱い、COUNTとCOUNT DISTINCTの違い、平均の再集計不可、measureの粒度要件(1行=1ファクト)を説明できることが重要です。
Analytics Engineer
問題 1
dbt Semantic Layerで月次売上合計とユニーク購入者数を正しく定義したい。適切なmeasure定義の組み合わせとして最も妥当なのはどれか。
正解: A
売上合計はsumで金額列を明示、ユニーク購入者数はcount_distinctで対象ID列を明示するのが正解。avgは平均のため合計には不適切。countは行数であり、顧客の一意数にはならない。
measureのcountでexprを省略すると何を数える?
多くの実装で行数をカウントします。特定列のNULLを除外したい場合はCOUNT(col)に相当するよう、対象列をexprで明示してください。
平均や率はmeasureで直接定義すべき?
再集計時の歪みを避けるため、分子(例: sum_revenue)と分母(例: order_count)を別々のmeasureとして定義し、利用側で計算するのが安全です。
count_distinctはなぜ非加法なのか?
集合の重複を潰すため、異なる期間やディメンションで算出した値を単純合算しても、重なり分が二重計上されません。したがって粒度を上げると元の値と一致しないことがあります。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
dbt Model の基礎: SQL で定義する変換の最小単位
Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...
dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点
dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...
dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点
dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...
dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト
Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....
dbt_project.yml の読み方:主要設定と命名を最短で掴む
dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...