catch-img

分析用とCDP用のデータモデルを考える ーローデータから2つの目的を満たす設計論

はじめに

データ基盤とデータマートを設計していると、こんな疑問にぶつかることがあります。

「分析チームが使うデータマート」と、「CDPやMAに連携するためのデータマート」は、同じ構造でいいのかな?

結論から言うと、同じ構造では足りません。 2つは目的がまったく異なるため、あるべきテーブルの形も変わってきます。

この記事では、その違いを整理したうえで、広告媒体・GA4・メール・CRMといった複数のローデータソースから、2種類のデータマートを効率よく構築するための設計パターンを紹介します。

目次[非表示]

  1. 1.はじめに
  2. 2.2つのデータマートは「目的」が違う
    1. 2.1.分析用マート:「何が起きたか?」を集計する
    2. 2.2.CDP用マート:「誰に届けるか?」を絞り込む
  3. 3.ローデータから2つを作るには「共通の中間層」が鍵
    1. 3.1.レイヤーの役割を整理する
  4. 4.設計の核心:IDスティッチング
  5. 5.統合中間層から2つのマートへ
    1. 5.1.分析マート(ディメンショナルモデル)
    2. 5.2.CDPマート(ワイドテーブル)
  6. 6.よくある落とし穴
  7. 7.dbtでの実装イメージ
  8. 8.まとめ

2つのデータマートは「目的」が違う

まず前提として、なぜ2種類が必要なのかを整理します。

分析用マート:「何が起きたか?」を集計する

BIツールやダッシュボードで使う分析用データマートは、集計のための構造が必要です。

「チャネル別×月別の売上は?」「新規ユーザーの流入経路は?」——こうした問いに答えるには、集計の軸(ディメンション)を自由に切り替えられるスタースキーマが適しています。ファクトテーブルを中心に、日付・ユーザー・チャネル・商品などのディメンションテーブルが放射状に配置される構造です。

主な操作は GROUP BY による集計。出力は数値・グラフ・KPIです。

CDP用マート:「誰に届けるか?」を絞り込む

一方、CDPや広告配信・MAツールに連携するためのデータは、最終的な出力がユーザーIDのリストです。

「直近30日未購買かつLTV上位20%のユーザー」を抽出するには、複合条件を1テーブルに横断して走らせる必要があります。JOINを極力減らし、1ユーザー1行のワイドテーブルに属性・行動サマリ・セグメントフラグを事前計算して持つ構造が適しています。

主な操作は WHERE による条件抽出。出力はユーザーIDのリストです。

観点

分析用マート

CDP用マート

主な問い

何が起きたか?

誰に届けるか?

主な操作

集計(GROUP BY)

条件抽出(WHERE)

出力

数値・グラフ・KPI

ユーザーIDリスト

テーブル構造

スタースキーマ(縦の構造)

ワイドテーブル(横の構造)

主語

イベント・期間・チャネル

ユーザー

一言でまとめると、分析用は「切り口を自由に変えられる縦の構造」、CDP用は「条件を横断して絞り込める横の構造」です。

ローデータから2つを作るには「共通の中間層」が鍵

では実際に、広告媒体・GA4・メール・CRMなどのローデータから、この2種類のマートをどう構築すればよいのでしょうか。

ここでよくある失敗が、ローデータから直接それぞれのマートを作ろうとすることです。変換ロジックが重複・分散し、ソースが変わるたびに両方の修正が必要になります。

正解は、共通の中間層を挟むアーキテクチャです。

レイヤーの役割を整理する

ステージング層は、ソースの形をそのまま保ちながら、型・命名だけを統一します。「広告のuser_idとCRMのcustomer_idは同じ概念だ」という判断はここでは行いません。ビジネスロジックは一切入れない、忠実な複製層です。

統合中間層が設計の核心です。複数ソースのIDを「同一人物」として名寄せするIDスティッチングを行い、全ソースの行動ログを縦積みで統合します。ここで作る int_user_identity(統合ユーザーID)が、下流の2つのマートが同じ「ユーザー」を主語にするための基盤になります。

設計の核心:IDスティッチング

複数のデータソースには、それぞれ異なるIDが存在します。

  • GA4には client_id

  • CRMには customer_id

  • メールツールには email_address

  • 広告プラットフォームには gclid や fbclid

これらを user_unified_id という共通キーに名寄せするのがIDスティッチングです。

この統合IDが確立されることで、「GA4で流入したユーザーが、その後CRMで購買した」という横断的な行動履歴が初めてつなぎ合わせられます。

IDスティッチングの設計品質が、下流すべての品質を決めると言っても過言ではありません。ここが甘いと、「同一人物への重複配信」や「購買済みユーザーへの新規獲得広告」といった施策ミスに直結します。

統合中間層から2つのマートへ

共通の中間層が整ったら、それぞれの目的に合わせて変換します。

分析マート(ディメンショナルモデル)

int_user_events の行動ログを集計・再構成し、スタースキーマに変換します。

1
2
3
4
5
fact_conversions
  ├── dim_date        (日付ディメンション)
  ├── dim_user        (int_user_identity から)
  ├── dim_channel     (ソース × キャンペーン情報)
  └── dim_product     (商品マスタ)

CDPマート(ワイドテーブル)

int_user_events をユーザー単位に集約し、属性・行動サマリ・セグメントフラグを横持ちにします。

1
2
3
4
5
6
7
8
user_master(1ユーザー1行)
  ├── ltv_total                (累計LTV)
  ├── purchase_count_30d       (直近30日購買回数)
  ├── last_purchase_date       (最終購買日)
  ├── last_channel             (最終接触チャネル)
  ├── email_open_rate_90d      (メール開封率)
  ├── is_vip                   (VIPフラグ)
  └── is_churn_risk            (離反リスクフラグ)

セグメントフラグを事前に計算して持つことで、MA・広告配信側との連携がシンプルになり、抽出クエリが WHERE is_vip = true AND purchase_count_30d = 0 のように直感的に書けます。

よくある落とし穴

この設計を理解していないと、現場でこんな問題が起きます。

分析用マートをCDP抽出に使おうとする → 毎回重いJOINが発生し、セグメント抽出が遅くなる。施策のリードタイムが伸びる。

CDP用のワイドテーブルで細かい分析をしようとする → 集計軸の柔軟性がなく、「チャネル別月次の推移」のような問いに答えられなくなる。

IDスティッチングをスキップして両マートを作る → 「同一人物」の行動がつながらず、分析もセグメントも精度が落ちる。

2つは目的が違うので、レイヤーを分けて設計するのが正解です。

dbtでの実装イメージ

Snowflake + dbt で構築する場合、フォルダ構成はこのようになります。

1
2
3
4
5
6
models/
  staging/          ← stg_google_ads, stg_ga4_events, ...
  intermediate/     ← int_user_identity, int_user_events
  marts/
    analytics/      ← fact_*, dim_*(分析マート)
    cdp/            ← user_master, user_segments(CDPマート)

intermediate/ が共有資産として機能し、ソースが変更されてもここを修正すれば下流両方に反映されます。変換ロジックの重複がなく、テストも書きやすい構成です。

まとめ

今回の内容を整理します。

  • 分析用マートとCDP用マートは目的が異なり、テーブル構造も変わる

  • ローデータから直接それぞれを作ろうとすると、ロジックが重複・分散して管理不能になる

  • 共通の統合中間層(IDスティッチング)を挟むのが理想のアーキテクチャ

  • 統合中間層から分析マート(縦の集計構造)とCDPマート(横の抽出構造)を別々に作る

  • dbtでは staging/ → intermediate/ → marts/analytics/ + marts/cdp/ の3層構成で整理できる

データ基盤を設計するときは、「このデータは何のために使われるのか」という問いから始めることが重要です。分析とCDPでは問いが違うため、答えとしてのデータ構造も変わる——その認識が、使われるデータ基盤を作る第一歩だと思います。

Matsumura
Matsumura

人気記事ランキング

タグ一覧