MetricFlow は、セマンティックモデルに定義したメジャー・ディメンション・エンティティを起点に、要求されたメトリックを最適な粒度・結合経路で集計し、最終的な SQL を生成します。SQL を直接書かずに一貫した定義で分析できる反面、モデル設計とクエリ指定が曖昧だと想定外の集計になり得ます。
本稿では、試験で問われやすいセマンティックモデルの構成要素と、MetricFlow のクエリ生成ロジックを実務目線で解説します。定義の置き場所や命名、結合経路の明示、時間粒度と time spine の扱いなど、現場での落とし穴も具体的に押さえます。
セマンティックモデルは、下流のレポートやアプリから独立して「ビジネス上の意味」を表す層です。MetricFlow はこの定義を読み取り、メジャー(集計対象)、ディメンション(切り口)、エンティティ(結合キー)を組み合わせてクエリを計画します。集計の時間軸は agg_time_dimension により既定化されます。
試験でも実務でも混同しやすいのが、メジャーとメトリックの違いです。メジャーはテーブル上の集計定義(sum(amount) など)、メトリックはメジャーを用いたビジネス指標(期間・フィルタ・比率などを含む)です。メジャーは再利用可能な最小単位、メトリックは意思決定単位と覚えると整理しやすいです。
| 要素 | 定義場所 | 主な属性 | 粒度/キー |
|---|---|---|---|
| セマンティックモデル | semantic_models: | model, entities, dimensions, measures | 行レベル定義に基づく |
| メジャー | measures: | name, agg, expr, filter | 定義テーブルの粒度 |
| ディメンション(時間) | dimensions: type: time | time_granularity, validity | time_granularity に依存 |
| ディメンション(分類) | dimensions: type: categorical | allowed_values 等 | エンティティ粒度に依存 |
| エンティティ | entities: | name, type(primary/foreign) | 結合キー |
| メトリック | metrics: | type, type_params, filter, window | 要求時の group_by と整合 |
最小構成のセマンティックモデルとメトリック定義例
semantic_models:
- name: orders
model: ref('stg_orders')
defaults:
agg_time_dimension: order_date
entities:
- name: order
type: primary
- name: customer
type: foreign
dimensions:
- name: order_date
type: time
type_params:
time_granularity: day
- name: status
type: categorical
measures:
- name: order_total
agg: sum
expr: amount
- name: order_count
agg: count
metrics:
- name: total_revenue
type: simple
type_params:
measure: order_total
filter: status = 'completed'MetricFlow は、要求された metrics と group_by、where、time_range から計画を立て、必要なセマンティックモデルを探索します。エンティティに基づいて結合経路を選び、時間粒度の整合を取り、必要に応じて time spine でギャップを埋めます。最後に集計順序を決定し、ターゲットのデータプラットフォーム向け SQL を生成します。
この過程で重要なのは、粒度上げ下げの順序と非加法メジャーの扱いです。まずメジャーの最小粒度で集計し、その後 group_by に合わせてロールアップします。比率や distinct 系は二重集計に弱いため、メジャー定義またはメトリック型で意図を明示し、不要な再集計を避けます。
MetricFlow のプランニングとSQL生成フロー
User Request
|
v
[Parse metrics/group_by/where]
|
v
[Semantic graph resolve]
| \
| [Choose join path by entities]
v
[Align time grain]
|
v
[Optional time spine (fill gaps)]
|
v
[Compute measures at base grain]
|
v
[Roll up to requested group_by]
|
v
[Apply metric-level filters/order/limit]
|
v
[Emit warehouse-specific SQL]リクエスト例と主なパラメータ
# 概念的なリクエスト表現例(実装環境により呼び出し方法は異なります)
metrics: [total_revenue]
group_by: [order_date__month, customer__country]
where: "status = 'completed'"
time_range: '2024-01-01 ~ 2024-03-31'
order: [order_date__month]
limit: 100時間ディメンションは、定義された time_granularity と、defaults.agg_time_dimension の組み合わせで意味づけされます。要求の group_by 側で月粒度を指定した場合、日粒度のメジャーは月にロールアップされます。メトリック側で期間指定を行うと、time_range に従って集計期間が制限されます。
データが欠損している期間も 0 行にしたい場合、MetricFlow は time spine(基準となる連続的な日付系列)を参照してギャップを補完します。これにより、例えば 2 月に注文が一件もなくても、レポート上は 0 として表現されます。
時間ディメンションと累積メトリックの定義例
semantic_models:
- name: orders
model: ref('stg_orders')
defaults:
agg_time_dimension: order_date
dimensions:
- name: order_date
type: time
type_params:
time_granularity: day
measures:
- name: order_total
agg: sum
expr: amount
metrics:
- name: revenue_7d
type: cumulative
type_params:
measure: order_total
window: 7 days
filter: status = 'completed'MetricFlow は、エンティティの primary/foreign に基づき、要求されたディメンションへ到達可能な最短の結合経路を選びます。orders から customers、さらに regions に到達するには、orders.customer → customers.customer → customers.region → regions.region のような連鎖が必要です。途中に複数経路がある場合でも、エンティティが一意にたどれるように設計しておくと誤結合を防げます。
事前集計済みテーブルやサマリーと明細を混在させる場合、エンティティ粒度の不一致が起きやすいです。ブリッジテーブルを経由して多対多を一意化する、あるいは派生セマンティックモデルを定義して粒度を合わせるなどの対策が有効です。
複数の結合経路があるときの定義例
semantic_models:
- name: customers
model: ref('dim_customers')
entities:
- name: customer
type: primary
- name: region
type: foreign
dimensions:
- name: country
type: categorical
- name: regions
model: ref('dim_regions')
entities:
- name: region
type: primary
dimensions:
- name: region_name
type: categorical
- name: orders
model: ref('fct_orders')
defaults:
agg_time_dimension: order_date
entities:
- name: order
type: primary
- name: customer
type: foreign
measures:
- name: order_total
agg: sum
expr: amount
# この構成で、group_by: [order_date__month, customers__country, regions__region_name] は
# customer → region の単一路で解決可能になるMetricFlow は、メジャー定義の filter を測定前に適用し、その後メトリック側の filter を集計段階に応じて適用します。例えば、メジャー order_total に status='completed' を入れると、以降の全てのメトリックで完了注文のみが対象になります。一方、メトリック側で filter を指定すれば、そのメトリックにだけ適用されます。
比率メトリックは、分母・分子のメジャーをそれぞれの正しい粒度で評価してから割り算する必要があります。group_by 側で余計なディメンションを入れると意図せず比率が分解されてしまうため、試験では「比率の粒度整合」を選択肢から見抜けるようにしておきましょう。
メジャーとメトリックのフィルタ差分の例
semantic_models:
- name: orders
model: ref('stg_orders')
measures:
- name: order_total_completed
agg: sum
expr: amount
filter: status = 'completed'
metrics:
- name: total_revenue_completed
type: simple
type_params:
measure: order_total_completed
- name: total_revenue_high_value
type: simple
type_params:
measure: order_total_completed
filter: amount >= 100Analytics Engineer 試験では、用語の厳密な理解と、粒度・結合・時間の整合を見抜く力が問われます。実務レビューと同じ観点で学ぶのが最短です。以下のチェックを開発・レビューに組み込むと、試験でも迷いにくくなります。
生成SQLの読解ポイントスニペット
-- 概要:ベース集計→結合→ロールアップ→最終整形 の順
with base as (
select
order_id,
customer_id,
order_date::date as order_date_day,
amount
from fct_orders
where status = 'completed'
),
measures as (
select
customer_id,
date_trunc('day', order_date_day) as d_day,
sum(amount) as order_total
from base
group by 1,2
),
rolled as (
select
date_trunc('month', d_day) as d_month,
sum(order_total) as total_revenue
from measures
group by 1
)
select * from rolled
order by d_month
limit 100;Analytics Engineer
問題 1
orders から customers と regions に到達可能なセマンティックモデルで、メトリック repeat_purchase_rate を「直近30日で2回以上購入した顧客数 ÷ 直近30日で購入した顧客数」として、group_by を order_date__month のみに設定して算出したい。結合経路の曖昧さと粒度の不一致を避けるために最も適切な前提はどれか。
正解: A
比率の分母分子を同一のエンティティ(customer)粒度で評価でき、orders→customers の結合経路が一意になること、さらに時間粒度が agg_time_dimension により安定化することが重要。B は不要なディメンションを混入させ粒度が崩れる。C は where では粒度の矯正ができない。D は空白期間が欠落し、期間比較が不安定になる。
セマンティックモデルの YAML はどこに置くのが一般的ですか?
dbt プロジェクトの models ディレクトリ配下で、対象モデルと近い位置に .yml として配置します。リネージとレビューのしやすさを優先し、staging か mart 単位でフォルダを分け、semantic_models セクションと metrics セクションを論理的に整理します。
結合経路が複数あって解決が不安定です。どうすればよいですか?
エンティティの primary/foreign を明確化し、到達経路が単一になるように中間モデル(ブリッジ)を設計してください。多対多を直接結合せず、エンティティキーの一意化を保証します。必要なら派生セマンティックモデルを用意して、比率や distinct を安全に計算できる粒度に揃えます。
生成された SQL を確認してデバッグできますか?
はい。MetricFlow は最終的に各プラットフォーム向けの SQL を生成します。実行環境のログやコンパイル結果から、ベース集計・結合・ロールアップの各段階を確認し、where の適用位置や group_by の展開が意図どおりかを検証してください。粒度の不一致は CTE 境界の列と group by 列を点検すると発見しやすいです。
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)、設定優先度...