ひとことで言うと#
データウェアハウス(DWH)のデータ構造を、分析しやすく・拡張しやすく・運用しやすい形に設計する体系的手法。ビジネスプロセスを起点にファクト(事実)とディメンション(切り口)に分解し、論理モデルから物理モデルまで一貫した設計を行う。Kimballのディメンショナルモデリングが最も広く使われている。
押さえておきたい用語#
- ファクトテーブル(Fact Table)
- ビジネスの計測可能な事象(売上、クリック、注文等)を記録するテーブル。数値指標(メジャー)と外部キーで構成される。
- ディメンションテーブル(Dimension Table)
- ファクトを分析する切り口(顧客、商品、日付、地域等)を格納するテーブル。「誰が・何を・いつ・どこで」にあたる。
- スタースキーマ(Star Schema)
- ファクトテーブルを中心にディメンションテーブルを放射状に配置する基本モデル。クエリがシンプルでパフォーマンスに優れる。
- スノーフレークスキーマ(Snowflake Schema)
- ディメンションテーブルをさらに正規化して分割したモデル。ストレージ効率は上がるが、結合が増えクエリが複雑になる。
- Slowly Changing Dimension(SCD)
- ディメンションの属性が時間とともに変化する場合の管理手法。Type 1(上書き)・Type 2(履歴保持)・Type 3(前後値保持)がある。
DWHモデリングの全体像#
こんな悩みに効く#
- DWHにテーブルが乱立し、どれを使えばいいか誰もわからない
- 同じ指標なのにチームごとに数値が違う(定義のズレ)
- SQLが複雑すぎて、簡単な集計でも結合を10テーブル以上書く
- データモデルの変更が下流のダッシュボードを壊してしまう
基本の使い方#
DWH全体を一度に作るのではなく、最も分析ニーズの高いプロセスから着手する。
- 経営層やアナリストにヒアリングし、「最も頻繁に問われる問い」を特定する
- 売上分析、マーケティング効果測定、在庫管理など1つのプロセスに絞る
- Kimballの「バス行列」(ビジネスプロセス × ディメンション)で全体像を描くと優先順位がつけやすい
ファクトテーブルの1行が何を表すかを定義する。これがモデル設計で最も重要な決定。
- 粒度はできるだけ細かくする(注文行、クリック、トランザクション単位)
- 細かい粒度から集約はできるが、粗い粒度から詳細に分解はできない
- 「この1行は、1件の注文の1商品を表す」のように、自然言語で明確に記述する
粒度に基づいて、計測する数値と分析の切り口を定義する。
- ファクト(メジャー): 加算可能な数値(金額、個数、時間等)
- ディメンション: 「誰が・何を・いつ・どこで・なぜ」にあたる属性
- 日付ディメンションは必ず作る(曜日・月・四半期・祝日フラグなど分析に必須)
- SCD(緩やかに変化するディメンション)の扱いも決めておく
論理モデルを実際のテーブルに変換し、想定クエリで検証する。
- dbt等のツールで変換ロジックを管理し、テスト可能な状態にする
- 代表的な分析クエリ(月次売上、チャネル別CVR等)を先に書いて、モデルが要件を満たすか確認する
- パフォーマンス(クエリ実行時間)も実データで検証する
- ドキュメント(カラム定義・粒度・ソースの出所)を必ず残す
具体例#
月商3億円のECサイト。BIツール(Tableau)のダッシュボードが47個乱立し、同じ「月次売上」でも数値が3通り存在していた。原因は、各アナリストがソーステーブルから独自にSQLを書いており、定義がバラバラだったこと。
スタースキーマで再設計:
ファクト: fact_order_lines(粒度: 注文×商品×行)
- order_id, product_id, customer_id, date_id, channel_id
- quantity, unit_price, discount_amount, revenue, tax
ディメンション:
dim_customers: customer_id, name, segment, prefecture, registration_date(SCD Type 2で履歴管理)dim_products: product_id, name, category_l1, category_l2, brand, supplierdim_dates: date_id, date, day_of_week, month, quarter, year, is_holidaydim_channels: channel_id, channel_name, channel_category
dbtで全変換を管理し、テスト210件を組み込んだ。移行後、「月次売上」の定義が1つに統一され、ダッシュボードを47個→15個に整理。アナリストが新しい分析を始める際のSQL作成時間が平均3時間→30分に短縮された。
BtoB SaaS企業(ARR 12億円、顧客数800社)で、MRR・チャーンレート・NDR(Net Dollar Retention)の計算がスプレッドシートベースだった。月初に経理が手動で集計しており、数値の確定に5営業日かかっていた。
サブスクリプション特有のモデリング:
ファクト: fact_subscription_events(粒度: 契約×イベント)
- subscription_id, customer_id, date_id, event_type(new / expansion / contraction / churn / reactivation)
- mrr_change, arr_change
このイベント粒度のファクトから、日次のfact_subscription_snapshotを自動生成:
- subscription_id, customer_id, date_id, status, current_mrr
ディメンション:
dim_customers: 企業規模・業種・契約開始日・CSM担当者dim_plans: プラン名・価格帯・機能セットdim_dates: 標準日付ディメンション
成果: MRR・チャーンレート・NDRが日次で自動計算され、確定が月初1日目に。経理の集計工数は月40時間→0時間。さらに、イベント粒度のファクトがあることで「チャーンの3か月前から利用頻度が低下している」というパターンを発見し、CS施策に活用できた。
1日5万件の配送を行う物流企業。配送遅延の原因分析に毎回2日かかっていた。基幹システム・GPS追跡システム・顧客管理システムの3つのソースからデータを手動で突合していたためだ。
段階的なモデリングアプローチ:
Phase 1(4週間): 配送ファクトのみ構築
fact_deliveries: 粒度=配送1件、delivery_id / date_id / driver_id / route_id / origin_id / destination_id / planned_time / actual_time / delay_minutes / status
Phase 2(3週間): ディメンション拡充
dim_drivers: ドライバー属性・所属拠点・免許種別dim_routes: ルート属性・距離・想定所要時間dim_locations: 拠点/配送先の住所・地域・種別
Phase 3(2週間): 派生ファクト追加
fact_delivery_daily_summary: 日次×拠点×ルート粒度の集約テーブルfact_delay_incidents: 遅延イベントに特化したファクト(遅延理由コード付き)
成果: 遅延原因の分析が2日→15分に短縮。「特定ルートの火曜午後に遅延が集中」というパターンを発見し、配車を最適化した結果、遅延率が12%→7.5%に改善。段階構築により、Phase 1の2か月後から分析チームが活用を開始でき、完成を待たずに価値を出せた。
やりがちな失敗パターン#
- 粒度を曖昧にしたまま設計する — 「1行が何を表すか」が定義されていないと、集計結果が二重カウントされたり、結合で行が膨れたりする。粒度の定義は最初に・厳密に
- すべてを1つの巨大ファクトに詰め込む — ビジネスプロセスが違うデータ(売上と在庫と問い合わせ)を1テーブルにまとめると、NULLだらけで意味が不明瞭になる。プロセスごとにファクトを分ける
- ディメンションにIDだけ入れる — ディメンションテーブルに名前や属性を入れず、毎回ソーステーブルに結合しに行く設計は、スタースキーマの利点を殺している
- 全体を一度に作ろうとする — 完璧なモデルを目指して半年かけるより、1つのビジネスプロセスを2〜4週間でモデリングして価値を出し、反復的に拡張する
まとめ#
DWHモデリングは、分析基盤のデータ構造をビジネスプロセスに基づいて設計する体系的手法である。ファクト(何を計測するか)とディメンション(どう切り口を分けるか)を明確にし、スタースキーマで組み立てることで、誰でも直感的にクエリが書ける状態を作る。最も重要な決定は粒度(Grain)の定義。1行が何を表すかが曖昧なままでは、どんなに美しいモデルでも集計結果は信頼できない。