dbt Semantic Layer で単一のセマンティックモデルで定義できる代表的なメトリクスタイプを試してみる

dbt Semantic Layer で単一のセマンティックモデルで定義できる代表的なメトリクスタイプを試してみる

2026.03.28

はじめに

dbt Semantic Layer では、YAML でメトリクスを定義することでビジネス指標を一元管理できます。
単一のセマンティックモデル上で定義できる代表的なメトリクスタイプ(Simple・Derived・Ratio・Cumulative)の定義方法と動作を確認した際の内容を記事としました。

dbt Semantic Layer のメトリクスタイプの概要

dbt Semantic Layer では、セマンティックモデルに対してメトリクスを YAML で定義します。メトリクスタイプによって集計のロジックや用途が異なり、目的に応じて使い分けることができます。
一部ですが、単一のセマンティックモデル上で定義できる代表的なメトリクスとして以下があります。

メトリクスタイプ 概要
Simple 単一カラムを1つの集計関数で集計する基本
Derived 既存のメトリクスを組み合わせた計算式を定義する
Ratio 分子・分母を明示して比率を計算する
Cumulative 時間軸に沿って値を累積集計する

https://docs.getdbt.com/reference/metric-properties?version=1.12

前提条件

検証環境

以下の環境を使用しています。

  • dbt
    • dbt Cloud
    • dbt CLI:0.40.15(2026-03-02 ビルド)
  • DWH:Snowflake

また、この記事では dbt Fusion エンジンおよび dbt platform Latest リリーストラックで利用可能な新しい YAML 記法(モデルの columns 配下にセマンティックモデル定義を記述する形式)を使用しています。

https://docs.getdbt.com/docs/build/latest-metrics-spec?version=1.12

事前準備

検証には jaffle_shop の公式サンプルデータを使用します。以下のテーブルを事前に用意しておきました。

ソース テーブル 説明
raw.jaffle_shop orders 注文データ(1行=1注文)

この環境については、以下の記事で用意したものをそのまま使用しています。

https://dev.classmethod.jp/articles/dbt-semantic-layer-flow-try/

検証開始時点で、モデル(models/stg_orders.sql)の定義は以下のようになっていました。
注意点として、amount 列をこちらで追加しています。

models/stg_orders.sql
select
    id as order_id,
    user_id as customer_id,
    order_date,
    status,
    abs(hash(id)) % 9901 + 100 as amount  -- ダミー金額(order_id ベースの決定論的な値、100〜10000)

from {{ source('jaffle_shop', 'orders') }}

また、YAML 定義は以下のようになっていました。

models/stg_orders.yml
# models/stg_orders.yml
version: 2

models:
  - name: stg_orders
    semantic_model:
      enabled: true
    agg_time_dimension: order_date

    columns:
      - name: order_id
        entity:
          type: primary
          name: orders

      - name: customer_id
        entity:
          type: foreign
          name: customer

      - name: order_date
        granularity: day
        dimension:
          type: time

      - name: status
        dimension:
          type: categorical

    metrics:
      - name: order_count
        type: simple
        label: 注文数
        agg: count
        expr: 1

      - name: distinct_customers
        type: simple
        label: ユニーク顧客数
        agg: count_distinct
        expr: customer_id

      - name: total_amount
        type: simple
        label: 合計金額
        agg: sum
        expr: amount

      - name: average_order_amount
        type: simple
        label: 平均注文金額
        agg: average
        expr: amount

その他注意点として、以降では特に明記しませんが、開発環境などで YAML 変更後はdbt parseを実行してからクエリする必要があります。

Simple メトリクス

概要

セマンティックモデル内の単一の列に対して直接的な集計を行うための基本的なメトリクスとなります。
シンプルメトリクスとして定義するには、メトリクス定義でtype: simpleと設定します。あわせて、agg:プロパティで具体的な集計方法を指定します。指定可能なオプションは以下の通りです。

agg 種類 説明
count 行数をカウント(expr: 1 で全行対象)
count_distinct 指定カラムの重複除去カウント
sum 合計値
average 平均値
median 中央値
min 最小値
max 最大値
percentile パーセンタイル値(percentilepercentile_type の追加指定が必須)
sum_boolean boolean カラムの true 件数を合計

https://docs.getdbt.com/docs/build/simple?version=1.12

具体的な定義例は以下の通りです。
name, type, label(BI ツールなどに表示される表示名), agg は必須です。
また、対象とある列名はexprで指定します。ここで指定がない場合、デフォルトではメトリクス名がそのまま使用されます。

models/stg_orders.yml
metrics:
  - name: order_count
    type: simple
    label: 注文数
    agg: count
    expr: 1             # COUNT(1) と同等。全行を対象にする慣用表現

  - name: distinct_customers
    type: simple
    label: ユニーク顧客数
    agg: count_distinct
    expr: customer_id   # このカラムの重複除去カウント

  - name: total_amount
    type: simple
    label: 合計金額
    agg: sum
    expr: amount

  - name: average_order_amount
    type: simple
    label: 平均注文金額
    agg: average
    expr: amount

基本的な定義に対してクエリしてみます。

$ dbt sl query --metrics order_count,distinct_customers,total_amount,average_order_amount
+----------------------+--------------------+-------------+--------------+
| AVERAGE_ORDER_AMOUNT | DISTINCT_CUSTOMERS | ORDER_COUNT | TOTAL_AMOUNT |
+----------------------+--------------------+-------------+--------------+
|          4908.989899 |                 62 |          99 |       485990 |
+----------------------+--------------------+-------------+--------------+

それぞれ以下の内容です。

  • order_count = 99(全注文数)
  • distinct_customers = 62(ユニーク顧客数)
  • total_amount = 485990(金額の合計)
  • average_order_amount = 4908.99(金額の平均)

通常の SQL でも確認すると同様の結果になっていることがわかります。

$ SELECT
      COUNT(1)                    AS order_count,
      COUNT(DISTINCT customer_id) AS distinct_customers,
      SUM(amount)                 AS total_amount,
      AVG(amount)                 AS average_order_amount
  FROM DBT_SL_DB.DBT_TYASUHARA.STG_ORDERS;
+-------------+--------------------+--------------+----------------------+      
| ORDER_COUNT | DISTINCT_CUSTOMERS | TOTAL_AMOUNT | AVERAGE_ORDER_AMOUNT |
|-------------+--------------------+--------------+----------------------|
|          99 |                 62 |       485990 |          4908.989899 |
+-------------+--------------------+--------------+----------------------

行レベルの計算式

exprプロパティでは、集計の対象となる列名を指定できますが、ここではカラム名だけでなく行レベルの計算式を直接記述できます。また、この場合は行単位で計算してから集計するという動作になります。
例として、以下のメトリクスを追加します。

  - name: total_tax_amount
    type: simple
    label: 消費税額合計
    agg: sum
    expr: amount * 0.1    # 行ごとに amount * 0.1 を計算してから SUM する

この場合、生成される SQL は以下のようになります。

$ dbt sl query --metrics total_tax_amount --compile
SELECT
  SUM(amount * 0.1) AS total_tax_amount
FROM dbt_sl_db.dbt_tyasuhara.stg_orders stg_orders_src_10000
LIMIT 100

フィルタ

また、メトリクス定義に絞り込み条件を固定することもできます。dbt コマンドでクエリ時に--whereを使用することもできますが、メトリクスと定義することで他のメトリクスからの利用も可能です。

  - name: returned_order_count
    type: simple
    label: 返品注文数
    agg: count
    expr: 1
    filter: "{{ Dimension('orders__status') }} = 'returned'"
    # {{ Dimension('entity名__dimension名') }} の形式でディメンションを参照する

生成される SQL は以下のようになります。WHERE 句として集計前に適用されます。

$ dbt sl query --metrics returned_order_count --compile

SELECT
  SUM(returned_order_count) AS returned_order_count
FROM (
  SELECT
    status AS orders__status
    , 1 AS returned_order_count
  FROM dbt_sl_db.dbt_tyasuhara.stg_orders stg_orders_src_10000
) subq_2
WHERE orders__status = 'returned'
LIMIT 100

percentile

percentile もシンプルメトリクスで使用できる集計関数の一つとして用意されています。
単一の列に対して、特定のパーセンタイル値を計算したい場合に使用されます。

agg: percentileを指定することでパーセンタイルでの集計となり、追加で以下の2つのパラメータが必須となります。

  • percentile: 計算したいパーセンタイルの数値を小数で指定します
    • こちらだと、1より大きな整数での指定ですが、私の環境ではエラーとなり、0〜1 の小数のみ有効でした。
  • percentile_type: パーセンタイルの計算方式として、discrete(離散型) または continuous(連続型) を指定します

ここでは discrete と continuous のメトリクスを定義します。

  - name: p90_order_amount_discrete
    type: simple
    label: 注文金額 P90 (discrete)
    agg: percentile
    expr: amount
    percentile: 0.9
    percentile_type: discrete

  - name: p90_order_amount_continuous
    type: simple
    label: 注文金額 P90 (continuous)
    agg: percentile
    expr: amount
    percentile: 0.9
    percentile_type: continuous

出力は以下のようになります。

$ dbt sl query --metrics p90_order_amount_discrete,p90_order_amount_continuous
+-----------------------------+---------------------------+
| P90_ORDER_AMOUNT_CONTINUOUS | P90_ORDER_AMOUNT_DISCRETE |
+-----------------------------+---------------------------+
|             9018.5997593403 |                      9101 |
+-----------------------------+---------------------------+

通常の SQL と比較します。

>SELECT
    PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY amount) AS continuous,
    PERCENTILE_DISC(0.9) WITHIN GROUP (ORDER BY amount) AS discrete    
FROM DBT_SL_DB.DBT_TYASUHARA.STG_ORDERS;
+------------+----------+                                                       
| CONTINUOUS | DISCRETE |
|------------+----------|
|   9018.600 |     9101 |
+------------+----------+

https://docs.snowflake.com/ja/sql-reference/functions/percentile_cont

上記より、discreteは値を昇順に並べたときの90番目の値が返っていることが確認できます。continuousは求めたパーセンタイルの順位位置が実際の1行にちょうど一致しない場合に、前後2つの値を線形補間して返します。

join_to_timespine / fill_nulls_with

join_to_timespine: truefill_nulls_with: 0プロパティを使うと、対応する時系列がない場合もタイムスパインから補完され、ゼロ埋めが可能です。

例として、すでに定義済みのメトリクスを日別で集計してみます。
出力は以下のようになり、注文が無い日付は出力に含まれません。

$ dbt sl query --metrics order_count \                                                                             
    --group-by metric_time__day \
    --order-by metric_time__day \
    --limit 10

+-------------------------+-------------+
| METRIC_TIME__DAY        | ORDER_COUNT |
+-------------------------+-------------+
| 2018-01-01T00:00:00.000 |           1 |
| 2018-01-02T00:00:00.000 |           1 |
| 2018-01-04T00:00:00.000 |           1 |
| 2018-01-05T00:00:00.000 |           2 |
| 2018-01-07T00:00:00.000 |           1 |
| 2018-01-09T00:00:00.000 |           1 |
| 2018-01-11T00:00:00.000 |           1 |
| 2018-01-12T00:00:00.000 |           1 |
| 2018-01-14T00:00:00.000 |           2 |
| 2018-01-15T00:00:00.000 |           1 |
+-------------------------+-------------+

そこで、join_to_timespine: trueと指定の以下のメトリクスを定義します。

  - name: daily_order_count
    type: simple
    label: 日次注文数
    agg: count
    expr: 1
    join_to_timespine: true

上記に対して同じようにクエリすると以下のようになります。実際の注文の無い日もタイムスパインテーブルの期間がある場合、すべて返されます。

$ dbt sl query --metrics daily_order_count \
    --group-by metric_time__day \
    --order-by metric_time__day \
    --limit 10

+-------------------------+-------------------+
| METRIC_TIME__DAY        | DAILY_ORDER_COUNT |
+-------------------------+-------------------+
| 2017-01-01T00:00:00.000 | <nil>             |
| 2017-01-02T00:00:00.000 | <nil>             |
| 2017-01-03T00:00:00.000 | <nil>             |
| 2017-01-04T00:00:00.000 | <nil>             |
| 2017-01-05T00:00:00.000 | <nil>             |
| 2017-01-06T00:00:00.000 | <nil>             |
| 2017-01-07T00:00:00.000 | <nil>             |
| 2017-01-08T00:00:00.000 | <nil>             |
| 2017-01-09T00:00:00.000 | <nil>             |
| 2017-01-10T00:00:00.000 | <nil>             |
+-------------------------+-------------------+

以下のようにfill_nulls_with: 0を追加します。

  - name: daily_order_count
    type: simple
    label: 日次注文数
    agg: count
    expr: 1
    join_to_timespine: true
    fill_nulls_with: 0

この場合、値がゼロ埋めされます。

$ dbt sl query --metrics daily_order_count \
    --group-by metric_time__day \
    --order-by metric_time__day \
    --limit 10

+-------------------------+-------------------+
| METRIC_TIME__DAY        | DAILY_ORDER_COUNT |
+-------------------------+-------------------+
| 2017-01-01T00:00:00.000 |                 0 |
| 2017-01-02T00:00:00.000 |                 0 |
| 2017-01-03T00:00:00.000 |                 0 |
| 2017-01-04T00:00:00.000 |                 0 |
| 2017-01-05T00:00:00.000 |                 0 |
| 2017-01-06T00:00:00.000 |                 0 |
| 2017-01-07T00:00:00.000 |                 0 |
| 2017-01-08T00:00:00.000 |                 0 |
| 2017-01-09T00:00:00.000 |                 0 |
| 2017-01-10T00:00:00.000 |                 0 |
+-------------------------+-------------------+

サンプルデータは2018年以降なので、以下のようにフィルタ可能です。

$ dbt sl query --metrics daily_order_count,order_count \
    --group-by metric_time__day \
    --order-by metric_time__day \
    --limit 10 \
    --where "{{ TimeDimension('metric_time', 'day') }} >= '2018-01-01'"
+-------------------------+-------------------+-------------+
| METRIC_TIME__DAY        | DAILY_ORDER_COUNT | ORDER_COUNT |
+-------------------------+-------------------+-------------+
| 2018-01-01T00:00:00.000 |                 1 | 1           |
| 2018-01-02T00:00:00.000 |                 1 | 1           |
| 2018-01-03T00:00:00.000 |                 0 | <nil>       |  
| 2018-01-04T00:00:00.000 |                 1 | 1           |
| 2018-01-05T00:00:00.000 |                 2 | 2           |
| 2018-01-06T00:00:00.000 |                 0 | <nil>       |  
| 2018-01-07T00:00:00.000 |                 1 | 1           |
| 2018-01-08T00:00:00.000 |                 0 | <nil>       |  
| 2018-01-09T00:00:00.000 |                 1 | 1           |
| 2018-01-10T00:00:00.000 |                 0 | <nil>       |  
+-------------------------+-------------------+-------------+

join_to_timespineのないorder_countで注文がない日は行自体が存在せず(<nil>となる)、daily_order_countjoin_to_timespine: true + fill_nulls_with: 0)では、注文がない日も行が生成され0で補完されます。折れ線グラフなど連続した時系列が必要な場合に有効なオプションです。

Derived メトリクス

概要

既存の他のメトリクスを組み合わせて、任意の計算式を用いた計算を行うためのメトリクスです。
Simple メトリクスのexprでも計算式を使用できました。この場合は、行レベルの計算が先でしたが、Derived メトリクスでは、集計後の結果に対して計算式が適用されます。

定義

以下では Derived メトリクスと関連する simple メトリクスのみ抜粋していますが、Derived メトリクスを定義する際は、input_metrics:で使用するメトリクスを指定し、expr:で計算式を設定します。

注意点として、ここでは単一のセマンティックモデル(同じテーブル内の既存のメトリクス)でのDerived メトリクスのため、models:ブロックの中で、semantic_model:と同じ階層にあるmetrics:の下に定義します。

本記事では扱いませんが、複数のモデルをまたぐ(クロスモデル)Derived メトリクスの場合、異なるセマンティックモデルのメトリクスを使用するので、models:ブロックの外側となる、YAMLファイルの最上位階層のmetrics:に定義します。

version: 2

models:
  - name: stg_orders
    semantic_model:
      enabled: true
    agg_time_dimension: order_date  # metric_time として自動公開される

    columns:
      - name: order_id
        entity:
          type: primary
          name: orders  # "order" はSQL予約語のため "orders" を使用。このname が --group-by の prefix になる(例: orders__status)

      - name: customer_id
        entity:
          type: foreign
          name: customer

      - name: order_date
        granularity: day  # 最小粒度。week/month/year への集計も可能
        dimension:
          type: time




    metrics:
      - name: order_count
        type: simple
        label: 注文数
        agg: count
        expr: 1

      - name: distinct_customers
        type: simple
        label: ユニーク顧客数
        agg: count_distinct
        expr: customer_id

      - name: total_amount
        type: simple
        label: 合計金額
        agg: sum
        expr: amount

      # --- Derived メトリクス ---

      - name: orders_per_customer
        type: derived
        label: 顧客あたり注文数
        # expr: input_metrics で宣言したメトリクス名を変数として使った計算式
        expr: order_count / distinct_customers
        # input_metrics: expr 内で使うメトリクスを列挙する。
        # ここで宣言したメトリクスが expr 内で変数として参照できる。
        input_metrics:
          - name: order_count
          - name: distinct_customers

      - name: amount_per_order
        type: derived
        label: 注文あたり金額
        expr: total_amount / order_count
        input_metrics:
          - name: total_amount
          - name: order_count

      - name: order_growth_rate
        type: derived
        label: 前月比注文数成長率
        # alias: 同一メトリクスを複数回参照するときに expr 内での変数名を区別する。
        # offset_window: 指定期間分だけ過去の値を参照する。
        expr: (current_orders - prior_orders) / prior_orders
        input_metrics:
          - name: order_count
            alias: current_orders       # expr 内での参照名
          - name: order_count
            alias: prior_orders
            offset_window: 1 month      # 1ヶ月前の値を参照

https://docs.getdbt.com/reference/metric-properties?version=1.12

実際にクエリしてみます。

# 顧客1人あたり平均注文数(全体)
$ dbt sl query --metrics orders_per_customer
+---------------------+
| ORDERS_PER_CUSTOMER |
+---------------------+
|            1.596774 |
+---------------------+

# 顧客1人あたり月別の平均注文数
$ dbt sl query --metrics orders_per_customer --group-by metric_time__month
+-------------------------+---------------------+
| METRIC_TIME__MONTH      | ORDERS_PER_CUSTOMER |
+-------------------------+---------------------+
| 2018-01-01T00:00:00.000 |            1.208333 |
| 2018-02-01T00:00:00.000 |                1.08 |
| 2018-03-01T00:00:00.000 |            1.129032 |
| 2018-04-01T00:00:00.000 |                   1 |
+-------------------------+---------------------+

--compile オプションで生成される SQL を確認すると、input_metricsが1つのサブクエリにまとめられ、外側でexprの計算式が適用される構造になっていることが分かります。

$ dbt sl query --metrics orders_per_customer --compile

SELECT
  order_count / distinct_customers AS orders_per_customer
FROM (
  SELECT
    SUM(1) AS order_count
    , COUNT(DISTINCT customer_id) AS distinct_customers
  FROM stg_orders
) subq_5
LIMIT 100

offset_windowの利用

offset_window は、Derived メトリクス専用のパラメータで、参照するメトリクスの対象期間を指定した期間分だけ過去にずらして値を取得するための機能です。
成長率や前月比などの計算に使用できます。

注意点として、計算式内で同じメトリクスを複数回参照する場合はaliasで別名をつけ、変数名を区別します。また、offset した先の期間がタイムスパインに含まれている必要があります(例:2018-01 の1ヶ月前 = 2017-12 を参照するにはタイムスパインが2017年から必要)。

以下では、注文数の前月からの成長率を定義しています。

  - name: order_growth_rate
    type: derived
    label: 前月比注文数成長率
    # alias: 同一メトリクスを複数回参照するときに expr 内での変数名を区別する。
    # offset_window: 指定期間分だけ過去の値を参照する。
    expr: (current_orders - prior_orders) / prior_orders
    input_metrics:
      - name: order_count
        alias: current_orders       # expr 内での参照名
      - name: order_count
        alias: prior_orders
        offset_window: 1 month      # 1ヶ月前の値を参照。単位: day / week / month / quarter / year など

この場合、出力は以下のようになります。また offset_window を使うメトリクスは metric_time での group-by が必須です。

$  dbt sl query --metrics order_count,order_growth_rate --group-by metric_time__month --order-by metric_time__month
+-------------------------+-------------+-------------------+
| METRIC_TIME__MONTH      | ORDER_COUNT | ORDER_GROWTH_RATE |
+-------------------------+-------------+-------------------+
| 2018-01-01T00:00:00.000 | 29          | <nil>             |
| 2018-02-01T00:00:00.000 | 27          | -0.068966         |
| 2018-03-01T00:00:00.000 | 35          | 0.296296          |
| 2018-04-01T00:00:00.000 | 8           | -0.771429         |
| 2018-05-01T00:00:00.000 | <nil>       | <nil>             |
+-------------------------+-------------+-------------------+

また、上記で--group-by metric_time__dayのようにして、オフセット期間(offset_window)とクエリ時の集計粒度(group-by)の組み合わせを自由に指定することが可能です。
MetricFlow では、まず最小粒度(日次など)で未集計のデータセットを取得し、指定された期間(1ヶ月)ずらしてオフセット結合を行います。その後、クエリで指定された集計粒度(日次や月次)に合わせてデータを丸めて集計し、最後に計算式が適用されます。

https://docs.getdbt.com/docs/build/derived#derived-metric-offset

Ratio メトリクス

概要

分子(numerator)・分母(denominator)を明示的に定義してその比率を計算するメトリクスです。Derived でも同様の計算は可能ですが、Ratio は分子÷分母という構造が定義から直接読み取れるため、割合・比率の計算に特化した用途では意図が明確になります。

定義

以下のように、numeratordenominatorにメトリクスを指定します。
注意点として、分母と分子で同じメトリクスを使う場合、aliasを指定しないと SQL 内で名前が衝突し、結果が常に 1 になります。

# alias なし(結果が 1 になる)
  - name: return_rate
    type: ratio
    numerator:
      name: order_count
      filter: "{{ Dimension('orders__status') }} = 'returned'"
    denominator:
      name: order_count

# alias あり(正しく動作する)
  - name: return_rate
    type: ratio
    numerator:
      name: order_count
      alias: returned_orders    # 分子側に別名をつけて参照先を分離
      filter: "{{ Dimension('orders__status') }} = 'returned'"
    denominator:
      name: order_count

実際にクエリしてみます。

# 返品率(全体)
$ dbt sl query --metrics return_rate
+--------------+
|  RETURN_RATE |
+--------------+
| 0.0404040404 | 
+--------------+

# 月別返品率:返品ゼロは nil
$ dbt sl query --metrics return_rate --group-by metric_time__month
+-------------------------+--------------+
| METRIC_TIME__MONTH      | RETURN_RATE  |
+-------------------------+--------------+
| 2018-01-01T00:00:00.000 | 0.1379310345 |  
| 2018-02-01T00:00:00.000 | <nil>        |  
| 2018-03-01T00:00:00.000 | <nil>        |
| 2018-04-01T00:00:00.000 | <nil>        |
+-------------------------+--------------+

https://docs.getdbt.com/docs/build/ratio?version=1.12

Cumulative メトリクス

概要

時間軸に沿って値を累積集計するメトリクスです。windowで固定幅の移動集計、grain_to_dateで暦の区切りに合わせたリセットタイミングを定義できます。
以下のようなイメージです。

設定 説明
なし(全期間) 全期間累積 累計注文数、累計売上
window: 30 days 直近30日の移動集計 直近30日の注文数
grain_to_date: month 月初からリセット(MTD) 月初来注文数

windowgrain_to_dateは同時使用できません。

累積メトリクスは非加算(non-additive)です。月別などの粗い粒度でクエリする際は、period_agg で「期間内のどの日次累積値を代表値とするか」を指定します。

period_agg 説明
指定なし(デフォルト) 期初日時点の累積値
last 期末日時点の累積値 月末残高、月末時点の累計
first 期初日時点の累積値(デフォルトと同値) 月初時点の残高
average 期間中の日次累積値の平均 月中平均在庫数など

MetricFlow は内部でタイムスパインと JOIN して最小粒度(日次)の累積値を先に計算し、その後、月別などのクエリ時に period_agg のウィンドウ関数で代表値を選ぶ、という2段階で処理します。累積値は非加算なので単純なSUMでの再集約ができず、period_aggによる明示的な指定が必要です。

なお最小粒度は agg_time_dimensionに指定した時間ディメンションのgranularityで決まります。このモデルではgranularity: dayのため日次が最小粒度です。

agg_time_dimension: order_date     # 参照する時間ディメンション

- name: order_date
  granularity: day                 # この設定が最小粒度になる
  dimension:
    type: time

定義

累積メトリクスを定義する際は、type: cumulativeを指定した上で、上述のパラメータを使用します。

metrics:
  - name: cumulative_orders
    type: cumulative
    label: 累積注文数(全期間)
    input_metric: order_count
    # window や grain_to_date の期間指定がないため、記録されている全期間の累計(Running Total)を計算

  - name: orders_mtd
    type: cumulative
    label: 注文数(月初来)
    input_metric: order_count
    grain_to_date: month # 毎月の初めに累積値がリセットされる

  - name: rolling_7d_orders
    type: cumulative
    label: 直近7日注文数
    input_metric: order_count
    window: 7 days # 常に過去7日間をスライドするウィンドウで累積値を計算

  - name: cumulative_orders_first
    type: cumulative
    label: 累積注文数(期初値)
    input_metric: order_count
    period_agg: first # period_agg: first により、要求された集計粒度(月次など)における「最初の値」を代表値として返す

  - name: cumulative_orders_last
    type: cumulative
    label: 累積注文数(期末値)
    input_metric: order_count
    period_agg: last # period_agg: last により、要求された集計粒度(月次など)における「最後の値」を代表値として返す

  - name: cumulative_orders_avg
    type: cumulative
    label: 累積注文数(期間平均)
    input_metric: order_count
    period_agg: average # period_agg: average により、要求された集計粒度(月次など)における累積値の「平均値」を代表値として返す

実際にクエリしてみます。

全期間累積(月別・日別)

# 月別:各月1日時点の累積値が返る(period_agg 未指定のデフォルト)
$ dbt sl query --metrics cumulative_orders --group-by metric_time__month \
  --where "metric_time__month <= '2018-04-01'"
+-------------------------+-------------------+
| METRIC_TIME__MONTH      | CUMULATIVE_ORDERS |
+-------------------------+-------------------+
| 2018-01-01T00:00:00.000 |                 1 | 1/1 当日分のみ
| 2018-02-01T00:00:00.000 |                29 | 〜2/1 まで(= 1月全体)
| 2018-03-01T00:00:00.000 |                58 | 〜3/1 まで
| 2018-04-01T00:00:00.000 |                91 | 〜4/1 まで
+-------------------------+-------------------+

この結果から、period_agg を指定しない累積メトリクスを月単位で集計した場合、各月の期初日時点の累積値が返ることが確認できます。

例えば、1月は 1/1 に注文が1件あり、集計結果としてもその1件のみが返っています。2月の29は、2月の月間件数ではなく、2/1 時点での累積注文数、つまり1月末までに積み上がった累積値 に相当します。
このことから、月粒度で返される値は、各月の初日を含む時点の累積値であることが分かります。

そのため、月別の累計推移を確認する場合には利用できますが、「その月の最終的な累積値」を見たい場合はperiod_agg: lastを使う必要があります。

続けて、日別でクエリしてみます。

# 日別:累積の増加が確認しやすい
$ dbt sl query --metrics cumulative_orders,order_count \
  --group-by metric_time__day \
  --where "metric_time__day <= '2018-01-10'" \
  --order-by metric_time__day
+-------------------------+-------------------+-------------+
| METRIC_TIME__DAY        | CUMULATIVE_ORDERS | ORDER_COUNT |
+-------------------------+-------------------+-------------+
| 2018-01-01T00:00:00.000 |                 1 | 1           |
| 2018-01-02T00:00:00.000 |                 2 | 1           |
| 2018-01-03T00:00:00.000 |                 2 | <nil>       | # 注文なし(前日と同値)
| 2018-01-04T00:00:00.000 |                 3 | 1           |
| 2018-01-05T00:00:00.000 |                 5 | 2           |
| 2018-01-06T00:00:00.000 |                 5 | <nil>       |
| 2018-01-07T00:00:00.000 |                 6 | 1           |
| 2018-01-08T00:00:00.000 |                 6 | <nil>       |
| 2018-01-09T00:00:00.000 |                 7 | 1           |
| 2018-01-10T00:00:00.000 |                 7 | <nil>       |
+-------------------------+-------------------+-------------+

日別で見ると、挙動が分かりやすいです。
order_count に値がある日はその分だけ cumulative_orders が増加し、注文がない日は order_count が<nil>でも、累積値は前日から据え置きとなっています。

period_agg の比較

period_agg:ごとの違いを確認してみます。

$ dbt sl query \
  --metrics cumulative_orders,cumulative_orders_first,cumulative_orders_last,cumulative_orders_avg \
  --group-by metric_time__month --where "metric_time__month <= '2018-04-01'" \
  --order-by metric_time__month
+-------------------------+-------------------+-------------------------+------------------------+-----------------------+
| METRIC_TIME__MONTH      | CUMULATIVE_ORDERS | CUMULATIVE_ORDERS_FIRST | CUMULATIVE_ORDERS_LAST | CUMULATIVE_ORDERS_AVG |
+-------------------------+-------------------+-------------------------+------------------------+-----------------------+
| 2018-01-01T00:00:00.000 |                 1 |                       1 |                     29 |                13.935 |
| 2018-02-01T00:00:00.000 |                29 |                      29 |                     56 |                41.928 |
| 2018-03-01T00:00:00.000 |                58 |                      58 |                     91 |                73.741 |
| 2018-04-01T00:00:00.000 |                91 |                      91 |                     99 |                  97.9 |
+-------------------------+-------------------+-------------------------+------------------------+-----------------------+

この結果からは、period_agg によって月粒度で返される代表値が変わることが分かります。
cumulative_orders と cumulative_orders_first は同じ値になっており、どちらも期初日時点の累積値を返しています。

一方、cumulative_orders_last はその月の最終日時点での累積値を返すため、月末時点の到達値を確認したい場合はこちらが適しています。
また、cumulative_orders_avg は月内の各日の日次累積値を平均したもので、月の途中経過をならした代表値として解釈できます。

rolling_7d_orders(直近7日移動集計・日別)

order_count(当日の注文数)と同時取得し移動集計の動きを見てみます。

$ dbt sl query --metrics order_count,rolling_7d_orders --group-by metric_time__day \
  --where "metric_time__day <= '2018-01-15'" --order-by metric_time__day
+-------------------------+-------------+-------------------+
| METRIC_TIME__DAY        | ORDER_COUNT | ROLLING_7D_ORDERS |
+-------------------------+-------------+-------------------+
| 2018-01-01T00:00:00.000 | 1           |                 1 |
| 2018-01-02T00:00:00.000 | 1           |                 2 |
| 2018-01-03T00:00:00.000 | <nil>       |                 2 |
| 2018-01-04T00:00:00.000 | 1           |                 3 |
| 2018-01-05T00:00:00.000 | 2           |                 5 |
| 2018-01-06T00:00:00.000 | <nil>       |                 5 |
| 2018-01-07T00:00:00.000 | 1           |                 6 | # 1/1〜1/7 の7日間に含まれる注文数
| 2018-01-08T00:00:00.000 | <nil>       |                 5 | # 1/1 の注文が集計範囲から外れるため、当日の注文がなくても 6 → 5 に減少
| 2018-01-09T00:00:00.000 | 1           |                 5 |
| 2018-01-10T00:00:00.000 | <nil>       |                 5 |
| 2018-01-11T00:00:00.000 | 1           |                 5 |
| 2018-01-12T00:00:00.000 | 1           |                 4 |
| 2018-01-13T00:00:00.000 | <nil>       |                 4 |
| 2018-01-14T00:00:00.000 | 2           |                 5 |
| 2018-01-15T00:00:00.000 | 1           |                 6 |
+-------------------------+-------------+-------------------+

この結果から、window: 7 daysを指定した累積メトリクスは、直近7日間の範囲だけを集計する移動ウィンドウとして動作していることが分かります。
そのため、新しい注文が入れば値は増えますが、過去の注文がウィンドウの範囲外に出ると値が減ります。

例えば、2018-01-07 の6は、1/1〜1/7 の7日間に含まれる注文数です。
次の 2018-01-08 では 1/1 の注文が集計範囲から外れるため、当日の注文がなくても 6 → 5 に減少しています。

同様に 2018-01-12 では、1/5 の2件が範囲外に出る影響を受けて値が下がっています。
日々の変化をなめらかに見たい場合や、短期トレンドを確認したい場合に有効です。

orders_mtd(月初来累積・日別)

次に、grain_to_date: monthを指定した orders_mtd を日別で確認してみます。

$ dbt sl query --metrics orders_mtd --group-by metric_time__day   --where "metric_time__day <= '2018-04-01'" --order-by metric_
time__day
+-------------------------+------------+
| METRIC_TIME__DAY        | ORDERS_MTD |
+-------------------------+------------+
| 2018-01-01T00:00:00.000 |          1 |
| 2018-01-02T00:00:00.000 |          2 |


| 2018-01-30T00:00:00.000 |         28 |
| 2018-01-31T00:00:00.000 |         29 |
| 2018-02-02T00:00:00.000 |          2 |
| 2018-02-03T00:00:00.000 |          2 |


| 2018-02-27T00:00:00.000 |         26 |
| 2018-02-28T00:00:00.000 |         27 |
| 2018-03-01T00:00:00.000 |          2 |
| 2018-03-02T00:00:00.000 |          3 |


| 2018-03-30T00:00:00.000 |         34 |
| 2018-03-31T00:00:00.000 |         35 |
| 2018-04-02T00:00:00.000 |          1 |
| 2018-04-03T00:00:00.000 |          3 |


| 2018-04-11T00:00:00.000 |          8 |
| 2018-04-12T00:00:00.000 |          8 |
+-------------------------+------------+

この結果から、orders_mtd は月初から当日までの累積値を返しており、月が変わるたびにリセットされることが確認できます。

1月中は cumulative_orders と同様に増加していきますが、2月に入ると 2月分の累積として再度 1 から積み上がります。3月、4月も同様で、それぞれの月初を起点に月内の注文数が累積されています。

https://docs.getdbt.com/docs/build/cumulative?version=1.12

さいごに

dbt Semantic Layer における代表的なメトリクスタイプの定義と動作を確認してみました。
こちらの内容がどなたかの参考になれば幸いです。

この記事をシェアする

FacebookHatena blogX

関連記事