Treasure Data CJO(Customer Journey Orchestration)完全解説-第2弾


前回の記事ではCJOの全体構造を解説した。「すごろく」のメタファーで全体像を掴んでもらった上で、今回はより深い実践内容に踏み込む。

特に、現場で「なぜ動かない?」「どうすれば修正できる?」という壁にぶつかったときに必要な知識を集中して解説する。


「外部セグメント参照」設計——これをやるかやらないかで運用コストが10倍変わる

なぜ外部セグメント参照が必須なのか

CJOの最大の制約は「起動後にジャーニーの構造を編集できない」ことだ。Entry Criteria・Milestone・Exit Criteriaなどの「各種基準」は起動後に一切変更できない。

しかし公式ドキュメントが強調している「抜け道」がある。

各種基準の中で直接ルールを書かず、外部のバッチセグメントをinclude / excludeするルールだけを書く」という設計だ。

【外部セグメント参照の構造】
CJO内の Entry Criteria:
「セグメントAをinclude」だけを記述 ← 起動後変更不可
↓ 参照
外部バッチセグメントA(オーディエンススタジオで管理):
「過去30日以内にサイト訪問 AND 未購買 AND ...」← 起動後でも自由に編集可能!

起動後も外部セグメントは自由に編集できる。よって「外部セグメントのルール変更 → CJOの実質的な基準変更」が実現できる。

「抜け道」を前提とした外部セグメントの命名規則

現場でおすすめする命名規則を紹介する。

命名規則の例
[JN_01] 購買育成ジャーニー - ステージ1 - エントリー基準
[JN_01] 購買育成ジャーニー - ステージ1 - 終了基準(ドロップアウトリスト)
[JN_01] 購買育成ジャーニー - ステージ1 - 終了基準(正常プロファイルexclude用)
[JN_01] 購買育成ジャーニー - ステージ2 - マイルストーン
[JN_01] 購買育成ジャーニー - ゴール基準

重要: 各外部セグメントは「そのCJOの基準以外からは参照されないようにする」ことが公式ドキュメントの推奨だ。他のジャーニーやセグメントから参照されていると、基準の変更が意図しない影響を与える可能性がある。

Entry Criteriaへの外部セグメント設定手順

セグメントエディタ上で「Drag-on Rules → Existing Segment」を選択すると、外部のバッチセグメントをinclude / excludeするルールが作れる。

Entry Criteriaの設定
1. セグメントエディタを開く
2. 「Drag-on Rules」から「Existing Segment」を選択
3. 「[JN_01] ステージ1 - エントリー基準」セグメントを選択
4. 「Include」を選択して保存
これだけ。実際の条件ロジックは全て外部セグメント側に書く。

Exit Criteriaの設計——「空のセグメントを2つ用意しておく」という鉄則

公式ドキュメントが強調している「すべてのステージに必ず終了基準を設定する」という推奨には、具体的な設計パターンがある。

なぜ全ステージにExit Criteriaが必要か

「終了基準が必要なさそうなステージ」でも、必ず以下の事態は発生する:

  1. Entry Criteriaの設定ミスで意図しないプロファイルがエントリーしてしまった
  2. JumpやRe-entryオプションの影響で意図せず再エントリーしたプロファイルの重複アクティベーションを止めたい
  3. 特定のオプトアウト顧客をシナリオから外したい

これらは「起動後に発生する」問題だ。Exit Criteriaがないステージでは、こうした緊急事態に対応できない。

推奨設計:空のセグメントを2種類事前作成する

事前に作成しておく外部セグメント(各ステージ用)
① ドロップアウトリスト用セグメント(初期は空)
→ 名前例:[JN_01] ステージ1 - 除外リスト(include用)
→ 「この中にいるプロファイルを除外したい」時に使う
② 正常プロファイル用セグメント(初期は空)
→ 名前例:[JN_01] ステージ1 - 正常プロファイル(exclude用)
→ 「この中にいるプロファイル以外を除外したい」時に使う

Exit Criteriaの設定

カスタム基準1(include):
「[JN_01] ステージ1 - 除外リスト」をinclude
→ 空のうちは誰も除外されない
→ 緊急時:除外したいプロファイルのtd_client_idをこのセグメントに追加する
カスタム基準2(exclude):
「[JN_01] ステージ1 - 正常プロファイル」をexclude
→ 空のうちは全員が「exclude条件を満たさない=誰も除外されない」状態
→ 使い方:逆に「正常なプロファイルのリスト以外を全員除外する」必要が出た時に活用

この設計なら、起動後でも外部セグメントを編集することで緊急対応が可能になる。

Stale Profileの「N+1日ルール」への注意

Exit Criteriaの「古くなったプロファイル(Stale Profile)」設定には重要な落とし穴がある。

公式ドキュメントの記述(重要)

Stale Profileの「Exit at = N日」設定をした場合、ステージのエントリーから数えて、N日ではなくN+1日後にドロップアウトする。

判定条件(公式ドキュメントより)
(ステージにエントリーした時のセッションタイムスタンプ)
< TD_TIME_ADD(journey WF 更新時のセッションタイムスタンプ, '-Nd')

「30日でドロップアウトさせたい」なら、設定値は29日にする必要がある。


journeyテーブルの読み方——デバッグの最重要スキル

CJOをデバッグする際、最も重要なのが「journey テーブル」の読み方だ。これはCJOが自動的に生成・更新するスナップショットテーブルで、各プロファイルが今どこにいるか、どのステップを通過したかが記録されている。

journeyテーブルの基本仕様

テーブル名:journey_${journey_id}
(例:journey_13)
記録されるタイムスタンプ:
セッションタイムスタンプ(実行された日の00:00のUnixタイム)
※実際の実行時刻ではなく、その日の0時0分のタイムスタンプ

カラムの値パターンと解釈

intimeouttime解釈
NULLでない値NULL今ちょうどここにいる
NULLでない値NULLでない値(同値)通過した(Activation Stepなどは必ず同値になる)
NULLでない値NULLでない値(異なる値)一度ここにいて、後で出た
NULLNULL今も過去もここにいたことがない

journeyテーブルの主要カラム

-- journeyテーブルの主要カラム構成
-- (ステージ0・ステージ1・ゴール・ドロップアウトの例)

SELECT
  cdp_customer_id,

  -- ジャーニー全体
  intime_journey,          -- ジャーニーにエントリーした時刻
  outtime_journey,         -- ジャーニーからドロップアウトした時刻

  -- ゴール
  intime_goal,             -- ゴール基準を満たした時刻

  -- ステージ0
  intime_stage_0,          -- ステージ0にエントリーした時刻
  outtime_stage_0,         -- ステージ0から出た時刻
  intime_stage_0_milestone,-- ステージ0のマイルストーンを満たした時刻
  intime_stage_0_exit_0,   -- ステージ0の終了基準0を満たした時刻

  -- ステージ0の各ステップ(ハッシュ値がステップIDになる)
  intime_stage_0_24d42ab6_activation,   -- Activation Stepを通過した時刻
  outtime_stage_0_24d42ab6_activation,  -- 同上(通過なのでintimeと同値)
  intime_stage_0_c48862a1_wait,         -- Wait Stepに入った時刻
  outtime_stage_0_c48862a1_wait,        -- Wait Stepを出た時刻(Wait日数後)

  -- ステージ1
  intime_stage_1,
  outtime_stage_1,
  intime_stage_1_milestone

FROM journey_13  -- journey_${journey_id}

現在進行中のプロファイルの位置を確認するSQL

-- 現在各プロファイルがどのステージ・ステップにいるかを確認する

SELECT
  cdp_customer_id,
  CASE
    WHEN intime_goal IS NOT NULL
    THEN 'GOAL'
    WHEN outtime_journey IS NOT NULL
    THEN 'DROPOUT'
    WHEN intime_stage_1 IS NOT NULL AND outtime_stage_1 IS NULL
    THEN 'ステージ1(進行中)'
    WHEN intime_stage_0 IS NOT NULL AND outtime_stage_0 IS NULL
    THEN 'ステージ0(進行中)'
    ELSE '未エントリー'
  END AS current_position,

  -- ステージ0のActivation Stepを通過済みかどうか
  CASE
    WHEN intime_stage_0_24d42ab6_activation IS NOT NULL
    THEN 'アクティベーション済み'
    ELSE '未アクティベーション'
  END AS activation_status

FROM journey_13
ORDER BY intime_journey DESC

アクティベーション済みプロファイルを集計するSQL

-- Activation Stepを通過したプロファイルの日別集計
-- (アクティベーションが実行されたプロファイルの実数確認)

SELECT
  TD_TIME_FORMAT(
    intime_stage_0_24d42ab6_activation,
    'yyyy-MM-dd', 'JST'
  )                                    AS activation_date,
  COUNT(DISTINCT cdp_customer_id)      AS activated_profiles
FROM journey_13
WHERE intime_stage_0_24d42ab6_activation IS NOT NULL
GROUP BY 1
ORDER BY 1 DESC

ゴール到達率・ドロップアウト率を集計するSQL

-- ジャーニー全体のファネル分析

WITH journey_summary AS (
  SELECT
    COUNT(DISTINCT cdp_customer_id)                    AS total_entered,
    COUNT(DISTINCT CASE
      WHEN intime_stage_1 IS NOT NULL
      THEN cdp_customer_id END)                        AS reached_stage1,
    COUNT(DISTINCT CASE
      WHEN intime_goal IS NOT NULL
      THEN cdp_customer_id END)                        AS reached_goal,
    COUNT(DISTINCT CASE
      WHEN outtime_journey IS NOT NULL AND intime_goal IS NULL
      THEN cdp_customer_id END)                        AS dropped_out
  FROM journey_13
  WHERE intime_journey IS NOT NULL
)
SELECT
  total_entered,
  reached_stage1,
  ROUND(100.0 * reached_stage1 / total_entered, 1)    AS stage1_rate_pct,
  reached_goal,
  ROUND(100.0 * reached_goal / total_entered, 1)      AS goal_rate_pct,
  dropped_out,
  ROUND(100.0 * dropped_out / total_entered, 1)       AS dropout_rate_pct
FROM journey_summary

起動後のジャーニーを編集したい

CJOの最大の難関が「起動後に構造を変えたいが変えられない」という制約への対処だ。公式ドキュメントが詳細手順を提供しているので、現場目線で整理する。

ケース①:「重複アクティベーションを許容する」場合(簡易版)

重複アクティベーション(元のジャーニーで既に配信を受けた顧客が、新ジャーニーでもう一度最初から配信を受ける)が問題ない場合は、単純な手順で対応できる。

手順:
1. 元のジャーニーをコピー(Duplicate Journey)
2. コピーしたジャーニーを編集
3. 元のジャーニーをポーズ
4. コピーしたジャーニーを起動
注意点:
元のジャーニーにいたプロファイルも、コピーしたジャーニーに最初からエントリーし直す。
つまり同じシナリオが重複して届く可能性がある。
テストジャーニーや影響が小さい場合は許容できる。

ケース②:「重複アクティベーションを避ける」場合(完全版)

本番運用中のジャーニーを変更する際は、より慎重な手順が必要だ。前提として「全ての基準で外部セグメント参照設計ができていること」が必要になる。

完全な手順(5ステップ):
STEP 1:ジャーニーをコピー
→ コピーしたジャーニーはドラフト状態・プロファイルは空
STEP 2:コピーしたジャーニーを編集
→ 変更したい部分を修正する
STEP 3:コピーしたジャーニーのEntry Criteriaに「元のジャーニーにいるプロファイルを除外」するルールを追加
→ セグメントエディタで「Journey (Stage)」から元のジャーニーを選択してexclude
→ これにより「元のジャーニーにいる既存プロファイル」がコピーしたジャーニーにエントリーしなくなる
STEP 4:元のジャーニーのEntry Criteria参照セグメントを「誰もエントリーできない状態」に変更
→ 外部セグメントのルールを「条件を満たすプロファイルが0になる条件」に変更
→ これにより新規プロファイルが元のジャーニーにエントリーしなくなる
STEP 5:STEP 4 と同日に、コピーしたジャーニーを起動
→ 翌日から新規プロファイルはコピーしたジャーニーにエントリーする
→ 元のジャーニーにいた既存プロファイルは元のジャーニーをそのまま進む
結果:
既存プロファイル → 元のジャーニーで継続(重複なし)
新規プロファイル → コピーしたジャーニーにエントリー

この手順が成立するためには「外部セグメント参照設計」が事前に完成していることが前提だ。これが「設計段階で外部セグメント参照を徹底する」理由のもう一つの根拠だ。


よくある失敗パターンと対処法——現場経験から

失敗パターン①:「アクティベーションが今日実行されない」

状況: journey WFは正常に完了しているが、今日アクティベーションが実行されない。

診断SQL

-- Activation Stepを本日通過したプロファイルがいるか確認
SELECT COUNT(*) AS count_today
FROM journey_13
WHERE intime_stage_0_24d42ab6_activation =
  TD_DATE_TRUNC('day', TD_SCHEDULED_TIME(), 'JST')

原因と対処:

  • 結果が0件 → プロファイルがまだActivation Stepに到達していない(Wait Step待ちなど)
  • 結果が0件 → Entry Criteriaを満たすプロファイルがそもそもいない(外部セグメントの条件確認)
  • 結果が0件でない → Activation WFがjourney WFより前に実行されている(スケジュール確認)

失敗パターン②:「ステージが進まない」

状況: プロファイルがステージ0にいるが、ステージ1に移動しない。

診断SQL

-- ステージ0にいてマイルストーンを満たしているが
-- ステージ1に移動していないプロファイルを確認
SELECT
  cdp_customer_id,
  intime_stage_0,
  intime_stage_0_milestone,
  intime_stage_1
FROM journey_13
WHERE
  intime_stage_0 IS NOT NULL
  AND outtime_stage_0 IS NULL          -- まだステージ0にいる
  AND intime_stage_0_milestone IS NOT NULL  -- マイルストーンを満たしている

原因: マイルストーンを満たしているのにステージ1に移動しない場合、多くは「外部セグメントの参照しているセグメントの更新が遅れている」か「journey WFがまだ実行されていない(翌日の更新で移動する)」ことが多い。CJOの更新は1日1回なので、マイルストーンを満たした日の翌日に移動するのが正常な動作だ。

失敗パターン③:「Wait Stepの日数が意図と違う」

状況: Wait Stepを5日に設定したが、実際の待機日数がずれている。

重要な理解: Wait Stepはjourney WFの更新回数でカウントする。「5日」とは「5回のjourney WF更新をまたいだ」という意味だ。ペアレントセグメントのスケジュールが毎日実行されていれば5日になるが、途中でポーズしたり、スケジュールが飛んだりすると日数がずれる。

確認SQL

-- Wait Stepの実際の滞在日数を確認
SELECT
  cdp_customer_id,
  intime_stage_0_c48862a1_wait,
  outtime_stage_0_c48862a1_wait,
  -- Wait Stepの実際の滞在日数を計算
  DATE_DIFF(
    'day',
    intime_stage_0_c48862a1_wait,
    outtime_stage_0_c48862a1_wait
  ) AS actual_wait_days
FROM journey_13
WHERE
  intime_stage_0_c48862a1_wait IS NOT NULL
  AND outtime_stage_0_c48862a1_wait IS NOT NULL
ORDER BY actual_wait_days DESC

セグメントエディタでのCJO専用機能——「Journey (Stage)」の使い方

CJOを起動すると、オーディエンススタジオのセグメントエディタに「Journey (Stage)」という専用のルール作成オプションが追加される。

Journey (Stage)で何ができるか

利用できる主なシナリオ
① 「ジャーニーXのステージYにいる顧客」をセグメント化
→ ジャーニーの進行状況に基づいてSQLでセグメントを作れる
→ 例:「ウィンバックジャーニーのステージ2にいる顧客へのオフライン施策」
② 「ジャーニーXにいる顧客」を別のジャーニーのEntry Criteriaからexclude
→ 「別のジャーニーに既にいる顧客はこのジャーニーにエントリーしない」
→ ジャーニー間の重複施策を防ぐ
③ 「ジャーニーXのゴールに到達した顧客」をセグメント化
→ コンバージョン済み顧客への施策を設計する

Duplicate Journey時のexclude設定(前章の完全手順 STEP3)はまさにこれを使う。

journeyテーブルをSQLで直接使う方法

セグメントエディタのJourney (Stage)機能でなく、SQLを直接書いてジャーニーの状態を分析したい場合

-- ジャーニーIDの特定
-- URL例:https://console-next.treasuredata.com/app/ps/257941/e/13/j/da
--   ペアレントセグメントID:257941 → テーブルプレフィックス:cdp_audience_257941
--   ジャーニーID:13 → テーブル名:journey_13

-- ただしjourney_13テーブルは cdp_audience_257941 データベースに存在する

SELECT *
FROM cdp_audience_257941.journey_13
WHERE intime_journey IS NOT NULL
LIMIT 100

CJO設計チェックリスト——起動前に必ず確認する10項目

長年の実装経験から導き出した「起動ボタンを押す前の最終確認リスト」だ。

起動前チェックリスト
【構造設計】
□ ステージ数は最大8つ以内か
□ ステップ数(全ステージ合計)は最大40以内か
□ 全てのパスの末尾にEnd Stepがあるか
□ Activation StepとActivation Stepの間に必ずWait Stepがあるか
【外部セグメント設計】
□ 全てのEntry Criteria / Milestone は外部セグメントをinclude/excludeするルールのみで構成されているか
□ 全てのExit Criteria は外部セグメントをinclude/excludeするルールのみで構成されているか
□ 各外部セグメントは他のジャーニー・セグメントから参照されていないか
□ 全ステージにExit Criteriaが設定されているか(空のセグメントで構わない)
【スケジュール設計】
□ ペアレントセグメントにdailyスケジュールが設定されているか
□ journey WFの実行完了時刻がActivation WFの実行開始時刻より前になっているか
【Activation Step設計】
□ String BuilderにJourney Name・Stage Nameを追加しているか(トラッキング用)
□ スケジュールがdailyになっているか(変更不可だが確認)
【Goal/Exit設計】
□ Goal Criteriaは「コンバージョンの本質的な定義」になっているか
□ Exit CriteriaのStale Profileの日数は N+1日ルールを考慮して設定されているか

まとめ:前回記事との組み合わせで完全理解を目指す

前回の記事(全体構造・すごろくメタファー・各ステップの基本)と今回の記事(外部セグメント設計・journeyテーブル・起動後編集手順)を合わせれば、CJOの実装に必要な知識の全体をカバーできる。

特に現場で最も価値が高い知識は以下の3つだ:

  1. 「外部セグメント参照設計」を最初から徹底すること — 後から後悔しない設計の基本
  2. 「journeyテーブルをSQLで読める」こと — デバッグとファネル分析の両方に使える
  3. 「起動後の完全編集手順(5ステップ)」を知っていること — 本番環境での変更対応に必須

CJOは「設計段階の一手間」が「運用段階の安心」に直結するツールだ。「後から直せないかもしれない」という緊張感を持ちながら丁寧に設計することが、長期的な成功への近道だ。


参考ドキュメント

ページURL
Entry Criteria / Milestone(外部セグメント参照の推奨)https://advanced.cdp-academy-lab.com/cjo/ch03_01.html
Exit Criteria(空セグメント2種類の推奨設計)https://advanced.cdp-academy-lab.com/cjo/ch03_03.html
Duplicate Journey(完全な起動後編集手順)https://advanced.cdp-academy-lab.com/cjo/ch04_01.html
Journey Tables without Jump(journeyテーブルの仕様)https://advanced.cdp-academy-lab.com/cjo/ch09_02.html
Tips:意図しないプロファイルの除外https://advanced.cdp-academy-lab.com/cjo/ch08_01.html
Tips:起動中のジャーニーを編集しなければならなくなった時https://advanced.cdp-academy-lab.com/cjo/ch08_02.html

MarTech Farmをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む