ディメンションは分析の“軸”であり、ファクトを意味付けする属性群です。dbtではモデル・スナップショット・テストを組み合わせて、安定したディメンションを構築します。
本稿はAnalytics Engineerの出題観点(モデリング、スナップショット、テスト、ドキュメント)を踏まえ、実務でそのまま使える設計パターンを解説します。
ディメンションは顧客・商品・日付などの属性テーブルで、分析時にグルーピング・フィルタの軸になります。ファクト(取引・イベント)と外部キーで結合し、スター・スキーマを形成します。
dbtでは、ディメンションを通常のモデルとして定義し、品質をスキーマテストで担保します。履歴を保持する場合はスナップショット(SCD2)を併用します。セマンティックな定義(粒度、キー、説明)をドキュメント化して、利用者が正しい解釈で参照できる状態を保つことが重要です。
| 要素 | 目的 | dbtでの実装ポイント |
|---|---|---|
| ディメンション表(顧客・商品・日付) | 分析の軸。属性を提供しグループ化・フィルタに使用 | モデルとして定義。surrogate key生成、not_null/uniqueテスト、必要に応じスナップショット |
| ファクト表(受注・決済・イベント) | 集計対象。メジャーの計算元 | 増分モデルが多い。ディメンションの外部キー参照テストを設定 |
| ブリッジ表(多対多中間) | 多対多関係の解消や階層の展開 | キーの複合一意性テスト、粒度の明記、結合順序のドキュメント化 |
スター・スキーマ(概念図)
シンプルなType1ディメンション(dim_customer.sql)
{{
config(materialized='table')
}}
with src as (
select
source_system,
customer_id,
coalesce(trim(lower(email)), '') as email,
trim(full_name) as full_name,
status,
first_order_date
from {{ source('crm', 'customers') }}
),
latest as (
-- 自然キーで最新のみを採用(重複除去例)
select * from (
select s.*,
row_number() over(partition by source_system, customer_id order by _ingested_at desc) as rn
from src s
) t where rn = 1
)
select
{{ dbt_utils.surrogate_key(['source_system','customer_id']) }} as customer_sk,
source_system,
customer_id,
email,
full_name,
status,
first_order_date
from latest;ディメンションの粒度は“行が一意に表す実世界の単位”です。顧客ディメンションなら「ソース×顧客ID」が自然キーになりやすく、これを安定化するためにサロゲートキー(ハッシュや連番)を採用します。
dbtではdbt_utils.surrogate_keyで複数列から安定ハッシュを生成できます。自然キーで重複を除去し、サロゲートキーにunique/not_nullテストをかけるのが基本パターンです。
キーとテストの宣言(schema.yml 抜粋)
version: 2
models:
- name: dim_customer
description: 顧客ディメンション(Type1)。source_system×customer_id粒度。
columns:
- name: customer_sk
tests:
- not_null
- unique
- name: customer_id
tests:
- not_null
- name: email
description: 正規化済みメールアドレス
属性履歴を保持したい場合、dbtのスナップショットを使ってSCD Type2を実装します。チェック対象カラムが変化したときに新しい行を挿入し、valid_from/valid_toで有効期間を管理します。
実務では、履歴保持用スナップショット(生の履歴)と、最新有効行だけを返す現在値ディメンション(便利用)を分けておくと集計が安定します。
顧客SCD2スナップショット(snapshots/dim_customer_scd2.sql)
{% snapshot dim_customer_scd2 %}
{{
config(
target_schema='snapshots',
unique_key='concat(source_system, ''::'', customer_id)',
strategy='check',
check_cols=['email','full_name','status'],
invalidate_hard_deletes=True
)
}}
select
source_system,
customer_id,
lower(email) as email,
full_name,
status
from {{ source('crm', 'customers') }}
{% endsnapshot %}
-- 最新有効行のみの便利用ディメンション(models/dim_customer_current.sql)
{{
config(materialized='view')
}}
select *
from {{ ref('dim_customer_scd2') }}
where dbt_valid_to is null同一ディメンションを文脈に応じて複数の役割で利用するのがロールプレイングです。代表例は日付ディメンションを受注日・出荷日などで使い分けるパターンです。
複数データマート間で同一解釈を維持するコンフォームド・ディメンションは、単一のソースモデルを参照し、ビュー層でカラム名のロールに応じたエイリアスを付けると運用が簡潔です。
ロールプレイング日付ディメンション(ビュー)
{{
config(materialized='view')
}}
-- models/dim_order_date.sql
select
date_sk as order_date_sk,
date_day as order_date,
year,
month,
week,
is_weekend
from {{ ref('dim_date') }};
{{
config(materialized='view')
}}
-- models/dim_ship_date.sql
select
date_sk as ship_date_sk,
date_day as ship_date,
year,
month,
week,
is_weekend
from {{ ref('dim_date') }};日付ディメンションは分析の中核です。欠損日のない連続軸(spine)を生成し、年・四半期・月・週・営業日フラグなどの派生列を付与します。連続軸は期間集計・ゼロ埋めに必須です。
dbt_utilsのdate_spineを使うとDWH差異を吸収して日付系列を生成できます。開始・終了日時は運用ポリシー(遡及範囲、将来日付の有無)に合わせて定義します。
日付ディメンション(dim_date.sql)
{{
config(materialized='table')
}}
with spine as (
{{ dbt_utils.date_spine(
datepart='day',
start_date="to_date('2018-01-01')",
end_date='current_date'
) }}
),
cal as (
select
cast(replace(cast(date_day as string), '-', '') as bigint) as date_sk,
date_day,
extract(year from date_day) as year,
extract(quarter from date_day) as quarter,
extract(month from date_day) as month,
extract(week from date_day) as week,
case when extract(dayofweek from date_day) in (6,7) then true else false end as is_weekend
from spine
)
select * from calディメンションは“変わらない軸”としての信頼性が重要です。キーの一意性・NULL禁止・関係の整合をスキーマテストで強制し、モデル説明やカラム定義、粒度、更新方針をドキュメント化します。
試験対策では、どのテストをどこに設定すべきか(dim側のunique/not_null、fact側のrelationships)がよく問われます。実務では、CIでスナップショット・モデル・テストを定期実行し、破壊的変更をdocsとカタログで可視化する運用が有効です。
関係テストとドキュメント(schema.yml 抜粋)
version: 2
models:
- name: fact_orders
description: 受注ファクト(日次増分)
columns:
- name: customer_sk
tests:
- relationships:
to: ref('dim_customer')
field: customer_sk
- name: product_sk
tests:
- relationships:
to: ref('dim_product')
field: product_sk
- name: order_date_sk
tests:
- relationships:
to: ref('dim_date')
field: date_sk
- name: dim_product
description: 商品ディメンション。粒度=product_id。価格などの履歴は別テーブルで管理。
columns:
- name: product_sk
description: サロゲートキー
tests: [not_null, unique]
- name: product_id
description: 自然キー(ソース依存)
tests: [not_null]
Analytics Engineer
問題 1
受注ファクトに対して、顧客ステータスの履歴を“当時の値”で分析したい。dbtの設計として最も適切なのはどれか。
正解: A
“当時の値”が必要なためSCD2が適切。スナップショットで有効期間を管理し、受注日が有効期間内の顧客行に結合する。Type1や常時デノーマライズでは履歴が失われ、日次フルリフレッシュはコスト・安定性の面で非効率。
ディメンションとエンティティテーブルは同じですか?
近い概念ですが目的が異なります。エンティティは業務データの正規化表現で、ディメンションは分析用に“軸”として最適化した表です。ディメンションでは分析に不要な列の削除、コード値の正規化、派生列の追加、サロゲートキー化、SCDの適用などを行います。
SCD2は必ずスナップショットで実装すべきですか?
dbtではスナップショットが安全で標準的です。DWHの変更データキャプチャ(CDC)を直接使う選択肢もありますが、移植性や一貫したテーブル構造(valid_from/valid_to、dbt_updated_at等)を重視するならスナップショットが扱いやすいです。
遅延到着ファクトはどう扱いますか?
ファクトのイベント日付に基づいてロールプレイング日付ディメンションに結合し、必要に応じて再集計(再実行)を許容する設計にします。ディメンション側はSCD2で“当時の属性”に結合できるよう有効期間を管理し、増分モデルでは過去再計算の再取り込みウィンドウを設けます。
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)、設定優先度...