dbt

MetricFlow の仕組みを理解する:セマンティックモデルとクエリ生成の実務知識

2026-04-19
NicheeLab編集部

MetricFlow は、セマンティックモデルに定義したメジャー・ディメンション・エンティティを起点に、要求されたメトリックを最適な粒度・結合経路で集計し、最終的な SQL を生成します。SQL を直接書かずに一貫した定義で分析できる反面、モデル設計とクエリ指定が曖昧だと想定外の集計になり得ます。

本稿では、試験で問われやすいセマンティックモデルの構成要素と、MetricFlow のクエリ生成ロジックを実務目線で解説します。定義の置き場所や命名、結合経路の明示、時間粒度と time spine の扱いなど、現場での落とし穴も具体的に押さえます。

セマンティックモデルの基本と用語整理

セマンティックモデルは、下流のレポートやアプリから独立して「ビジネス上の意味」を表す層です。MetricFlow はこの定義を読み取り、メジャー(集計対象)、ディメンション(切り口)、エンティティ(結合キー)を組み合わせてクエリを計画します。集計の時間軸は agg_time_dimension により既定化されます。

試験でも実務でも混同しやすいのが、メジャーとメトリックの違いです。メジャーはテーブル上の集計定義(sum(amount) など)、メトリックはメジャーを用いたビジネス指標(期間・フィルタ・比率などを含む)です。メジャーは再利用可能な最小単位、メトリックは意思決定単位と覚えると整理しやすいです。

  • セマンティックモデルは dbt の YAML で管理し、リネージは dbt の DAG と整合させる
  • エンティティは結合経路の選定に必須。primary と foreign を正しく付与する
  • 時間ディメンションは time_granularity と agg_time_dimension を明確に
  • メジャーは最小限で汎用的に、メトリックで文脈(フィルタ・期間・ウィンドウ)を付与
要素定義場所主な属性粒度/キー
セマンティックモデルsemantic_models:model, entities, dimensions, measures行レベル定義に基づく
メジャーmeasures:name, agg, expr, filter定義テーブルの粒度
ディメンション(時間)dimensions: type: timetime_granularity, validitytime_granularity に依存
ディメンション(分類)dimensions: type: categoricalallowed_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 のクエリ生成フロー

MetricFlow は、要求された metrics と group_by、where、time_range から計画を立て、必要なセマンティックモデルを探索します。エンティティに基づいて結合経路を選び、時間粒度の整合を取り、必要に応じて time spine でギャップを埋めます。最後に集計順序を決定し、ターゲットのデータプラットフォーム向け SQL を生成します。

この過程で重要なのは、粒度上げ下げの順序と非加法メジャーの扱いです。まずメジャーの最小粒度で集計し、その後 group_by に合わせてロールアップします。比率や distinct 系は二重集計に弱いため、メジャー定義またはメトリック型で意図を明示し、不要な再集計を避けます。

  • よくある誤動作の原因は、エンティティ未定義・曖昧な結合経路・時間粒度の不一致
  • group_by の指定は entity 経路で到達可能なディメンションに限定する
  • where はメジャー集計前に適用されるフィルタと、後段で適用される条件が混在し得るため定義レベルを意識する

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 として表現されます。

  • agg_time_dimension を未設定にしない
  • group_by の時間粒度は明示する(order_date__month など)
  • 累積や移動平均などウィンドウ系は、元のメジャー粒度とウィンドウ定義の整合を確認

時間ディメンションと累積メトリックの定義例

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 のような連鎖が必要です。途中に複数経路がある場合でも、エンティティが一意にたどれるように設計しておくと誤結合を防げます。

事前集計済みテーブルやサマリーと明細を混在させる場合、エンティティ粒度の不一致が起きやすいです。ブリッジテーブルを経由して多対多を一意化する、あるいは派生セマンティックモデルを定義して粒度を合わせるなどの対策が有効です。

  • primary はそのテーブルの自然キー、foreign は外部参照キーを指す
  • 多対多は中間モデルで解消する。直接結合は避ける
  • 到達不能な group_by を指定すると解決失敗や不要なカートesian拡張を招く

複数の結合経路があるときの定義例

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 側で余計なディメンションを入れると意図せず比率が分解されてしまうため、試験では「比率の粒度整合」を選択肢から見抜けるようにしておきましょう。

  • メジャー filter は強い制約。流用範囲を意識して最小限に
  • メトリック filter は用途ごとに付与。where と混同しない
  • 比率や distinct はダブルカウントに注意。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 >= 100

試験対策と実務チェックリスト

Analytics Engineer 試験では、用語の厳密な理解と、粒度・結合・時間の整合を見抜く力が問われます。実務レビューと同じ観点で学ぶのが最短です。以下のチェックを開発・レビューに組み込むと、試験でも迷いにくくなります。

  • メジャーとメトリックの責務分離:フィルタはどちらに置くべきか
  • agg_time_dimension は全モデルで設定済みか
  • エンティティの primary/foreign は妥当か、到達不能な group_by はないか
  • 比率メトリックで分母分子の粒度が一致しているか
  • time spine によるギャップ補完が期待どおりか
  • 生成 SQL を読み、ベース集計→ロールアップ→最終整形の層が分離されているか

生成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 のみに設定して算出したい。結合経路の曖昧さと粒度の不一致を避けるために最も適切な前提はどれか。

  1. customers で customer を primary、orders で customer を foreign と明示し、orders の agg_time_dimension を order_date に設定した上で、分子・分母を distinct customer ベースのメジャーとして定義する
  2. regions で region を primary とし、group_by に regions__region_name も常に含める
  3. orders のメトリックに where 句だけで customer 粒度を指定する
  4. time spine を無効化し、30日間にデータがない日は除外する

正解: 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 列を点検すると発見しやすいです。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
dbt

dbt Model の基礎: SQL で定義する変換の最小単位

Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...

dbt

dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点

dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...

dbt

dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点

dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...

dbt

dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト

Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....

dbt

dbt_project.yml の読み方:主要設定と命名を最短で掴む

dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...

dbtの記事一覧 (100件)
© 2026 NicheeLab All rights reserved.