購入後のステップ配信の効果測定は、送らない人を残して確かめる|毎日の分け方、初回購入から同じ日数での比べ方、続ける・直す・止めるの決め方

購入後のフォローを開いた人は2回目の購入が多い。でもそれは、もともと買う人が開いているだけかもしれません。毎日入ってくる初回購入者を顧客IDで分けて一部に送らず、初回購入から同じ日数で2回目の割合を比べ、要る人数がそろった日に続けるか・直すか・止めるかを決める進め方を、SQL と記録表つきで示します。

この記事をシェア:
目次 クリックで開く

送っても送らなくても2回目を買う確率が変わらないように作った購入者のデータで、購入後のステップ配信を数えてみました。それでも、メールを開いた人は開かなかった人より、2回目を買った割合が10ポイントほど高く出ました。

「もともと買う人が開いているだけでは」と聞かれて答えに詰まるのは、このためです。かといって一部の人に送るのをやめれば、その人の2回目の売上を逃さないか、という迷いも残ります。

購入後のステップ配信(初回購入のあと、決まった日数ごとに送るメールや LINE)を続けている、通販の CRM 担当の方向けです。一部の人に送らずにおき、増えた分だけを数えて続けるかを決めるまでを、SQL と記録表で追います。

同じ購入者を3通りに比べた横棒グラフ。初回購入から60日以内に2回目を買った割合は、1通以上開いた人 24.58%・1通も開かなかった人 14.39%で差 +10.19 ポイント。3通とも届いた人 10.55%・送らない人 19.68%で差 −9.12 ポイント。送る側全員 20.18%・送らない人 19.68%で差 +0.50 ポイント(差の幅 ±1.01)同じ購入者を3通りに比べた横棒グラフ。初回購入から60日以内に2回目を買った割合は、1通以上開いた人 24.58%・1通も開かなかった人 14.39%で差 +10.19 ポイント。3通とも届いた人 10.55%・送らない人 19.68%で差 −9.12 ポイント。送る側全員 20.18%・送らない人 19.68%で差 +0.50 ポイント(差の幅 ±1.01)
架空の購入者のデータです(以下の図も同じ)。作ったのは当社(通販の顧客データの集計を支援)で、検証用の BigQuery の上で、配信の効き目を0に、メールを開きやすい人ほどもともと買いやすくしてあります

同じ購入者(初回購入者 36,786人)を3通りに比べ、初回購入から60日以内に2回目を買った割合を並べています。効き目は0なので、正しい比べ方なら差は0の近くに出るはずです。そうなったのは、一番下の比べ方だけでした。上の2つで差が出る理由と、一番下の比べ方の作り方を、順に見ていきます。

決めたいのは、ステップ配信を続けるか・直すか・止めるか。始める前に4つを記録表に書く

確かめたいのは、たとえば初回購入の3日後・14日後・30日後に1通ずつ送るステップ配信を、このまま続けるか、中身を直すか、止めるかです。配信で増えた分は、送った人と送らなかった人の差で数えます。広告で、出した人と出さない人の差を増えた分と見るのと同じ考え方です(広告で増えた分の確かめ方の記事)。

送らない人(効果測定ではホールドアウトとも呼ばれます)を残して比べる考え方そのものは、LINE配信で増えた売上の測り方の記事と共通です。違うのは、ステップ配信には購入をきっかけに毎日だれかが入ってくるので、送らない人も毎日決め続けることです。

始める前に、記録表に4つを書きます。1つ目は見る日数で、初回購入から何日以内の2回目を数えるかです。最後の通を送る日より長く取ります。30日後に送る通のあとの購入は、30日で数えるとほとんど入らないからです。この記事では60日にしました。

2つ目は、続ける理由になる最小の差です。1人にステップ配信を送る費用を、2回目の注文1件の粗利で割ると、2回目を買う人の割合が何ポイント増えれば元が取れるかの目安になります(0.02 なら2ポイント)。この記事では、例として2ポイントを置いて進めます。3つ目の判断に要る送らない人の人数と、4つ目の確かめる日は、後の節で出し方を書きます。

4つを先に書くのは、結果を見てから選ぶと、判断が変わってしまうことがあるからです。後で出てくる判断の表で決めると、効き目0に作ったデータでも、30日で数えた差 −0.12 は「止める」、60日で数えた差 +0.50 は「直す」になりました。

集めるのは、注文・配信の同意・送った記録の3つの表

数えるのに要る表は3つです。1つ目は注文の表で、顧客ID・注文番号・注文日・金額(税抜)があれば足ります。初回購入日は、この表の顧客ごとの最初の注文日から出します。注文日は、日本時間の日付にそろえておきます。初回購入から同じ日数が経つのを待って2回目の割合を数える方法は、リピート分析の進め方の記事が詳しく、それを送る側と送らない人の2つに分けて使います。

2つ目は顧客の表で、配信に同意しているか(SQL の mail_optin)を持ちます。送らない人は、もともと送ってよい人の中から選ぶからです。同意の列が無ければ、配信停止の一覧に無い人、で代えます。

メールの場合、特定電子メール法の第3条は、広告・宣伝のためのメール(第2条第2号の「特定電子メール」)の決まりです。第1項は送ってよい相手、第2項は同意(または送信の求め)があったことを証する記録を残すこと、第3項は送らないよう求められたあとは送らないことです。送らないでおくことを制約する条文は、第2条・第3条を読んだ範囲では見当たりませんでした。購入の記録を配信の効き目の分析に使うなら、公表している利用目的に含まれているかも確かめます(個人情報保護法の改正とメール配信のデータの記事)。

3つ目は送った記録の表で、顧客ID・何通目か・送った日時・開いたかを持ちます。この表は比べるためではなく、送らない人に誤って送っていないかを見つけるために使います。開いたかの記録は、受け取る人の設定で正しく取れないことがあります。Apple のメールプライバシー保護を使うと、メールが開かれたかを送信者が確認できなくなると、Apple のサポートページにあります。

送らない人は、顧客IDから毎回同じ結果になる方法で、毎日決め続ける

ステップ配信には、毎日新しい初回購入者が入ってきます。名簿を一度に分けて終わりにはできないので、毎日、まだ分けていない初回購入者だけを分けて足していきます。大事なのは、同じ人を、いつ決め直しても同じ側に入れることです。あとで、誰がどちらだったかを出し直せるからです。

BigQuery では、FARM_FINGERPRINT という関数が使えます。同じ入力からは、実行をまたいでも同じ値を返すと公式ドキュメントにあり、決まった割合の行を抜き出す使い方も例に載っています。次の SQL は、顧客IDから0〜99の値(SQL の bucket)を出し、その値が割合より小さい人を送らない人にします。毎日1回実行します。

-- 送らない人を決める。毎日1回実行する。すでに分けた人の行は書き換えず、まだ分けていない初回購入者だけを足す(何度実行しても同じ結果)。
-- 割合を変えるときは、下の step_holdout_rules に「新しい割合と適用開始日」の行を足す。その日以降に初回購入した人から新しい割合になり、前に分けた人は変わらない。
--   例:INSERT INTO `your_project.crm.step_holdout_rules` (salt, holdout_pct, apply_from) VALUES ('step_after_first:', 30, DATE '2026-04-01');
--   割合を上げ下げするときは salt(分け方の文字列)を変えない(同じ文字列なら、20% の人は 30% の中にもそのまま入る)。
-- 送ってよい人だけで分ける:customers.mail_optin(配信の同意)が TRUE の人。同意の列が無い環境では、配信停止の一覧に無い人、の条件に置き換える。
DECLARE run_day DATE DEFAULT CURRENT_DATE('Asia/Tokyo');  -- この日までに初回購入した人を分ける

CREATE TABLE IF NOT EXISTS `your_project.crm.step_holdout_rules` (
  salt STRING,          -- 分け方の文字列(配信ごとに変える)
  holdout_pct INT64,    -- 送らない人の割合(%)
  apply_from DATE       -- この日以降に初回購入した人に使う
);
INSERT INTO `your_project.crm.step_holdout_rules` (salt, holdout_pct, apply_from)
SELECT 'step_after_first:', 20, DATE '2026-01-01'   -- 最初の1行(表が空のときだけ入る。始める前に値を決めて書き換える)
FROM (SELECT 1)
WHERE NOT EXISTS (SELECT 1 FROM `your_project.crm.step_holdout_rules`);

CREATE TABLE IF NOT EXISTS `your_project.crm.step_assign` (
  customer_id STRING,
  first_order_date DATE,
  bucket INT64,         -- 0〜99 の値
  grp STRING,           -- send(送る側)/holdout(送らない人)
  holdout_pct INT64,    -- 分けたときの割合
  assigned_on DATE      -- 分けた日
);

INSERT INTO `your_project.crm.step_assign` (customer_id, first_order_date, bucket, grp, holdout_pct, assigned_on)
WITH first_orders AS (
  SELECT customer_id, MIN(order_date) AS first_order_date
  FROM `your_project.crm.orders`
  GROUP BY customer_id
),
target AS (
  SELECT f.customer_id, f.first_order_date
  FROM first_orders AS f
  JOIN `your_project.crm.customers` AS c USING (customer_id)
  WHERE c.mail_optin = TRUE                                                -- 送ってよい人だけ
    AND f.first_order_date >= (SELECT MIN(apply_from) FROM `your_project.crm.step_holdout_rules`)
    AND f.first_order_date <= run_day
    AND NOT EXISTS (SELECT 1 FROM `your_project.crm.step_assign` AS a WHERE a.customer_id = f.customer_id)   -- まだ分けていない人だけ
),
with_rule AS (  -- 初回購入日に効いていた割合(適用開始日がいちばん新しい行)
  SELECT t.customer_id, t.first_order_date, r.salt, r.holdout_pct
  FROM target AS t
  JOIN `your_project.crm.step_holdout_rules` AS r ON r.apply_from <= t.first_order_date
  QUALIFY ROW_NUMBER() OVER (PARTITION BY t.customer_id ORDER BY r.apply_from DESC) = 1
)
SELECT
  customer_id,
  first_order_date,
  ABS(MOD(FARM_FINGERPRINT(CONCAT(salt, customer_id)), 100)) AS bucket,
  IF(ABS(MOD(FARM_FINGERPRINT(CONCAT(salt, customer_id)), 100)) < holdout_pct, 'holdout', 'send') AS grp,
  holdout_pct,
  run_day AS assigned_on
FROM with_rule;

SQL の salt は、配信ごとに変える「分け方の文字列」で、ここでは step_after_first: です。顧客IDの前にこの文字列を付けた値から、0〜99の値を出します。customer_id が数値の列なら、つなぐところを CAST(customer_id AS STRING) にします。あわせて、分けた結果の表(step_assign)の customer_id の型も、注文・顧客の表とそろえます。

比べる数字は、全員が配信に同意している前提で作ったデータで数えました。その初回購入者 36,786人を2回分けると、片方にしかいない人は0人でした。2回目の購入まで入った注文の表から分け直しても、結果は同じです。送らない人の割合は、月ごとに 20.10〜21.37% でした。

毎日実行する形と同意の条件は、このデータに配信の同意の列を足して確かめました。3月31日までの 16,794人を先に分けてから9月30日にもう一度実行すると、先に分けた人の行は1つも変わりませんでした。配信に同意していない初回購入者(2,886人)は、どちらの側にも入りません。

分け方の文字列を配信ごとに変えるのは、同じ人がどの配信からも外れるのを防ぐためです。誕生日クーポンの配信を別の分け方の文字列で2割分けると、両方で送らない人になったのは 1,487人で、この配信の送らない人の 19.8% でした。2割を別々に引いたときの、偶然の重なりと同じくらいです。

割合を途中で変える手順は、SQL の先頭のコメントにあるとおり、割合の表(step_holdout_rules)に行を1つ足すだけです。同意の列を足した同じデータで、4月1日から3割にする行を足して実行しました。送らない人の割合は、4月1日より前の初回購入者で 20.39%、以降で 30.52% になりました。割合を変えた日は、記録表にも新しい行として書きます。

全員を新しい割合で分け直すのは避けます。比べる数字に使った元のデータ(同意の列が無いもの)で、4月1日に2割から3割へ上げ、全員を分け直すと、4月1日より前に初回購入した 1,746人が送らない側に移りました。そのうち 1,691人には、すでに1通以上届いていました。

毎日の運用では、その日に初回購入した送る側の人だけを一覧にして、配信ツールに渡します。元のデータで3月15日の分を実行すると、167人の顧客IDが返りました。この一覧は分けた結果から作るので、送らない人を決める SQL を毎日実行していれば、割合を変えた日から新しい分け方の一覧になります。配信ツールに別の一覧を持たせているなら、それも同じ日に替えます。

-- その日に初回購入した人のうち、送る側だけ。配信ツールやオーディエンスに渡す一覧。
SELECT customer_id
FROM `your_project.crm.step_assign`
WHERE first_order_date = DATE '2026-03-15'
  AND grp = 'send'
ORDER BY customer_id;

メールなら、この一覧を、ステップ配信に入れる人として配信ツールに渡します。除外の一覧を持たせる機能があるかどうかは、ツールごとに確かめてください。LINE でも、一度きりのお知らせなら、絞り込み配信で送らない人のオーディエンスを「除外する」形が取れます(LINE配信で増えた売上の測り方の記事)。

LINE 公式アカウントのステップ配信は、開始条件に「友だち追加」か「オーディエンス」を選べます。条件分岐にもオーディエンスを置けます(マニュアル、2026年10月23日に確認)。ただ、配信先から外す設定はマニュアルに見当たらず、分岐の先にメッセージを置かない場合の動きも書かれていません。当社はどちらも試していません。

そこで考えられるのは、送る側の人だけを「ユーザーIDアップロード」のオーディエンスに入れ、それを開始条件にする形です。Messaging API 経由のオーディエンスも開始条件にでき、開始後に新しくオーディエンスに入った人も配信の対象になると、マニュアルにあります。送る側の人を毎日オーディエンスに足す方法は、LINE Developers の資料にあります。足した人に何日で配信が始まるかは確かめていません。ユーザーIDアップロードのオーディエンスは、作成から180日有効です。

顧客IDと LINE のユーザーIDのつなぎ方はLINEとCRMの連携の記事に、オーディエンスの作り方はオーディエンスの作り方の記事に書いています。

比べるのは送る側全員と送らない人。起点は初回購入の日にそろえる

比べる起点は、送った日ではなく初回購入の日です。送らない人には送った日がありません。両方とも、初回購入の翌日から60日以内に2回目を買ったかを数えます。比べる相手は送る側の全員で、届いた通数や、開いたかで絞りません。最初の図の上の2段は、絞ったときの比べ方です。

開いた人で絞ると差が出るのは、開きやすい人ほどもともと買いやすいからです。図の +10.19 ポイントの大きさは、データを作るときに決めた重なり具合で決まるので、自社の配信で同じ大きさになるわけではありません。言えるのは、開く人と買う人が重なっていれば、配信が効いていなくても差は出るということです。

LINE の Messaging API では、ユーザーがメッセージを読んだかを確認する方法は提供されていません。LINE Developers のよくある質問に、そう書かれています(LINEの配信を誰が開いたかは分かるかの記事)。開いた人で分ける表は、LINE ではそもそも作れません。メールでは作れてしまいます。

届いた人で絞ると低く出るのは、「購入したら止める」設定のためです。2回目を買った人にはその先の通を送らないので、3通とも届いた人は、30日後の通までに買わなかった人だけになります。図のデータでは、届いたのが0〜2通の 3,149人は全員が、次の通の前に2回目を買った人でした。−9.12 ポイントは、その偏りの分です。LINE でも、ブロックされるとステップ配信は止まるとマニュアルにあります。

比べる表は、次の SQL で出します。先頭の params で、確かめる日・見る日数・始めた日を決めます。確かめる日は、その日までの注文が、注文の表にすべて入っている日にします。確かめる日に初回購入から60日が経っていない人は外れます。確かめる日を8月15日にすると、数えるのは6月16日までに初回購入した人で、送る側は 26,939人、送らない人は 6,942人でした。only_pct は、割合を途中で変えたときに、割合ごとに分けて数えるための欄です。

-- 送る側と送らない人を、初回購入から n_days 日以内の2回目で比べる。確かめる日に n_days 日が経っていない人は外す。
WITH params AS (
  SELECT DATE '2026-09-30' AS check_day,   -- 確かめる日(この日までの注文で数える)
         60 AS n_days,                     -- 見る日数(初回購入から)
         DATE '2026-01-01' AS start_day,   -- 始めた日
         CAST(NULL AS INT64) AS only_pct   -- 割合を途中で変えたときは 20 や 30 を入れ、割合ごとに分けて実行する(NULL なら全員)
),
target AS (  -- 確かめる日までに、初回購入から n_days 日が経った人だけ
  SELECT a.customer_id, a.first_order_date, a.grp
  FROM `your_project.crm.step_assign` AS a, params
  WHERE a.first_order_date >= params.start_day
    AND DATE_ADD(a.first_order_date, INTERVAL params.n_days DAY) <= params.check_day
    AND (params.only_pct IS NULL OR a.holdout_pct = params.only_pct)
),
per_customer AS (  -- 初回購入の翌日から n_days 日以内の注文
  SELECT target.customer_id, target.grp,
    COUNT(o.order_id) AS orders_in_window,
    IFNULL(SUM(o.amount), 0) AS sales_in_window
  FROM target
  CROSS JOIN params
  LEFT JOIN `your_project.crm.orders` AS o
    ON o.customer_id = target.customer_id
   AND o.order_date > target.first_order_date
   AND o.order_date <= DATE_ADD(target.first_order_date, INTERVAL params.n_days DAY)
  GROUP BY target.customer_id, target.grp
),
by_group AS (
  SELECT grp, COUNT(*) AS people, COUNTIF(orders_in_window > 0) AS buyers,
    COUNTIF(orders_in_window > 0) / COUNT(*) AS rate, AVG(sales_in_window) AS sales_per_person
  FROM per_customer
  GROUP BY grp
)
SELECT
  s.people AS send_people, s.buyers AS send_buyers, ROUND(100 * s.rate, 2) AS send_rate_pct,
  h.people AS holdout_people, h.buyers AS holdout_buyers, ROUND(100 * h.rate, 2) AS holdout_rate_pct,
  ROUND(100 * (s.rate - h.rate), 2) AS diff_pt,
  ROUND(100 * 1.96 * SQRT(s.rate * (1 - s.rate) / s.people + h.rate * (1 - h.rate) / h.people), 2) AS margin_pt,
  ROUND(s.sales_per_person) AS send_sales_per_person, ROUND(h.sales_per_person) AS holdout_sales_per_person
FROM (SELECT * FROM by_group WHERE grp = 'send') AS s
JOIN (SELECT * FROM by_group WHERE grp = 'holdout') AS h ON TRUE;

SQL は、2回目の割合、差、差の幅、1人あたりの売上を返します。差の幅(95%)は、効き目が0のとき、分け方を変えて100回比べれば95回ほどは差がこの中に収まる、という幅です。1.96 は、その95%に合わせるための係数です。

初回購入からの日数ごとに、その日までに2回目を買った人の割合を並べると、次の図になります。

初回購入から0〜90日の、その日までに2回目を買った人の割合の線グラフ。効き目を入れたデータでは60日で送る側全員 23.46%、送らない人 20.48%、差 +2.98 ポイント(差の幅 ±1.03)。効き目0に作ったデータでは2本の線がほぼ重なり、差は −0.42〜+0.56 ポイントの間。送った日(3日・14日・30日)と見る日数(60日)に線初回購入から0〜90日の、その日までに2回目を買った人の割合の線グラフ。効き目を入れたデータでは60日で送る側全員 23.46%、送らない人 20.48%、差 +2.98 ポイント(差の幅 ±1.03)。効き目0に作ったデータでは2本の線がほぼ重なり、差は −0.42〜+0.56 ポイントの間。送った日(3日・14日・30日)と見る日数(60日)に線
効き目0に作ったデータでは、2本の線がほぼ重なる

読むのは、見る日数(60日)の線の上の2つの値です。効き目を入れたデータでは 23.46% と 20.48% で、差は +2.98 ポイント(差の幅 ±1.03)でした。効き目0に作ったデータでは、0〜90日のどの日も差が −0.42〜+0.56 ポイントの間に収まり、2本の線はほぼ重なります。

1か月分の差は、効き目が0でも揺れる。判断に要る人数を先に出す

毎月の報告に合わせて、初回購入の月ごとに差を出したくなります。ところが、このデータでは1か月分の送らない人は 1,143〜1,337人です。効き目0に作ったデータでも、月ごとの差は −1.79〜+1.98 ポイント動き、1か月分の差の幅は ±2.36〜2.57 ポイントと広くなりました。

初回購入の月ごとの差と差の幅の点グラフ。効き目0に作ったデータは −1.79〜+1.98 ポイント、効き目を入れたデータは +1.43〜+3.71 ポイント。人数を見ずに判断の表で決めると、効き目0に作ったデータは直す4か月・止める2か月、効き目を入れたデータは続ける5か月・直す1か月。3月の購入者を20通りに分け直した差は −1.54〜+1.56 ポイント初回購入の月ごとの差と差の幅の点グラフ。効き目0に作ったデータは −1.79〜+1.98 ポイント、効き目を入れたデータは +1.43〜+3.71 ポイント。人数を見ずに判断の表で決めると、効き目0に作ったデータは直す4か月・止める2か月、効き目を入れたデータは続ける5か月・直す1か月。3月の購入者を20通りに分け直した差は −1.54〜+1.56 ポイント
1か月分の差は差の幅が広く、人数を見ずに決めると判断が月ごとに割れる

図の下の段は、人数を見ずに判断の表で決めた場合の判断です。効き目0に作ったデータでも「直す」と「止める」が月ごとに入れ替わり、効き目を入れたデータでも「続ける」に「直す」が1か月混じりました。3月の点の横の小さな点は、同じ3月の購入者を、分け方の文字列を替えて20通りに分け直した差で、−1.54〜+1.56 ポイントに散らばっています。効き目が0でも、分け方だけでこれくらいの差は出ます。

そこで、判断に要る送らない人の人数を、始める前に出しておきます。次の表は、もとの2回目の割合・続ける理由になる最小の差・送らない人の割合から、差の幅が最小の差と同じになる人数を出したものです。

判断に要る送らない人の人数の表。もとの割合 10・20・30%、続ける理由になる最小の差 1・2・3ポイントごと。もとの割合20%・最小の差2ポイント・送らない人2割なら送らない人 1,921人(初回購入者 9,605人)。式は、送らない人の人数 ≧ 1.96² × p × (1 − p) × (1 + 1 ÷ k) ÷ M²判断に要る送らない人の人数の表。もとの割合 10・20・30%、続ける理由になる最小の差 1・2・3ポイントごと。もとの割合20%・最小の差2ポイント・送らない人2割なら送らない人 1,921人(初回購入者 9,605人)。式は、送らない人の人数 ≧ 1.96² × p × (1 − p) × (1 + 1 ÷ k) ÷ M²
この記事の例では、送らない人 1,921人がそろう日を待つ

この記事の例(もとの割合20%、最小の差2ポイント、送らない人2割)では、送らない人 1,921人、初回購入者で 9,605人が要ります。最小の差を1ポイントにすると、送らない人は 7,684人まで増えます。式は、2回目を買ったかどうかの割合の差にだけ使えます。1人あたりの売上の差には使えません。

送らない人には、確かめている間、2回目を促すメッセージが届きません。2割なら初回購入者の5人に1人、1割なら10人に1人です。1割に下げると、毎日届かない人の割合は半分になります。ただ、判断までに要る送らない人は 1,921人から 1,708人と、約11%しか減りません。要る初回購入者は 9,605人から 17,080人に増え、そろうまでの日数はおよそ1.8倍に延びます。それでも送らない人を残すのは、残さなければ、配信で増えた分が本当にあるのかを確かめる手段がないからです。効き目の無い配信なら、送る費用だけが出ていき続けます。

確かめる日に1回だけ、続ける・直す・止めるを決める

確かめる日は、送らない人の累計が要る人数に届いた初回購入日に、見る日数を足した日です。このデータでは、1月1日〜2月16日の初回購入で送らない人が 1,938人になり、60日を足した4月17日が1回目の確かめる日でした。その日に、比べる表の差と送らない人の人数から、次の判断の表で決めます。

判断の表(上の行から順に見る):送らない人が要る人数に足りなければまだ判断しない、差が最小の差以上なら続ける、差が0以下なら止める、それ以外は直す(1回まで)。効き目0に作ったデータは4月17日 +1.67 で直す、次の期間(初回購入 2月17日〜4月4日)の6月3日 −1.17 で止める。効き目を入れたデータは +2.07 と +3.29 で2回とも続ける。この人数で1回確かめると、効き目0の配信は続ける 2.5%・直す 47.4%・止める 50.2%判断の表(上の行から順に見る):送らない人が要る人数に足りなければまだ判断しない、差が最小の差以上なら続ける、差が0以下なら止める、それ以外は直す(1回まで)。効き目0に作ったデータは4月17日 +1.67 で直す、次の期間(初回購入 2月17日〜4月4日)の6月3日 −1.17 で止める。効き目を入れたデータは +2.07 と +3.29 で2回とも続ける。この人数で1回確かめると、効き目0の配信は続ける 2.5%・直す 47.4%・止める 50.2%
右側の2回目は、中身を直した版ではなく、同じステップ配信の次の期間に初回購入した人で数えた

表は上の行から順に見ます。送らない人が要る人数に足りなければ、差がいくつでも決めません。そろっていれば、差が最小の差以上で続ける、0以下で止める、その間なら直すです。

直すとは、送る日や中身を変えることで、1回までにしておきます。直したら、比べる表の SQL の start_day を直した日に替え、そこから送らない人が要る人数に届いた日に60日を足して、次の確かめる日にします。1月1日からの累計のままでは、1回目の人が混ざるからです。そこでも続けるに届かなければ止めます。回数を決めないと、効き目の無い配信が直すを繰り返し、いつまでも止まりません。

この人数で1回確かめたとき、効き目0の配信が続けるになるのは約2.5%でした。効き目がちょうど最小の差の配信が止めるになるのも、約2.5%です(乱数で20,000回ずつ試した割合)。直すを1回まで認めると、効き目0の配信が最後に続けるになるのは、計算すると約3.6%です。1回目で続けるになる 2.46%に、1回目で直すになる 47.36%のうち2回目で続けるになる 2.46%を足した値です。効き目0でも半分ほどは直すに入るので、直すは続ける理由にはなりません。

図の右側は、同じ表で2回決めた例です。効き目0に作ったデータでは、4月17日に +1.67 で直す、6月3日に −1.17 で止めるになりました。ただし6月3日の分は、中身を直した版ではなく、同じステップ配信で次の期間(2月17日〜4月4日)に初回購入した人を数えたものです。

判断まで SQL で出すには、比べる表の SQL の末尾の「;」を外し、全体を「WITH r AS (」と「)」で囲んで、そのあとに次の部分を足します。2.0 と 1921 は、自社の最小の差と要る人数に書き換えます。

SELECT r.*,
  CASE
    WHEN r.holdout_people < 1921 THEN 'まだ判断しない'
    WHEN r.diff_pt >= 2.0 THEN '続ける'
    WHEN r.diff_pt <= 0 THEN '止める'
    ELSE '直す'
  END AS decision
FROM r;

誤送信や途中の変更は、記録表に書き残してから数える

記録表は配信ごとに1行です。始める前に、配信の名前から確かめる日までを書き、確かめる日に結果と判断を書きます。

-- 配信ごとに1行。始める前に上の段を書き、確かめる日に結果と判断を書く。
-- IF NOT EXISTS なので、もう一度実行しても今までの記録は消えない(表が無いときだけ作る)。
CREATE TABLE IF NOT EXISTS `your_project.crm.step_tests` (
  program STRING,          -- 配信の名前
  start_date DATE,         -- 始めた日(この日以降に初回購入した人から分ける)
  salt STRING,             -- 分け方の文字列(顧客IDの前に付け、配信ごとに変える)
  holdout_pct INT64,       -- 送らない人の割合(%)
  n_days INT64,            -- 見る日数(初回購入から)
  min_diff_pt FLOAT64,     -- 続ける理由になる最小の差(ポイント)
  need_holdout INT64,      -- 判断に要る送らない人の人数
  check_day DATE,          -- 確かめる日
  diff_pt FLOAT64,         -- 結果:差(ポイント)
  margin_pt FLOAT64,       -- 結果:差の幅(±、95%)
  holdout_people INT64,    -- 結果:送らない人の人数
  decision STRING,         -- 判断:続ける・直す・止める・まだ判断しない
  note STRING              -- 記録:誤送信・途中で変えたこと など
);

確かめる日に結果を書き込み、その行を表示すると、次のようになりました(効き目を入れたデータ)。手順どおりなら、4月17日の1回目の確かめる日に最初の行(送らない人 1,938人・差 +2.07 ポイント・続ける)を書きます。ここで表示したのは、そのあとも送らない人を残して、6か月分をまとめて9月30日に数えた次の行です。

列書いた中身
配信の名前購入後フォロー(3日・14日・30日)
始めた日(この日以降に初回購入した人から分ける)2026-01-01
分け方の文字列(顧客IDの前に付け、配信ごとに変える)step_after_first:
送らない人の割合(%)20
見る日数(初回購入から)60
続ける理由になる最小の差(ポイント)2.0
判断に要る送らない人の人数1921
確かめる日2026-09-30
結果:差(ポイント)2.98
結果:差の幅(±、95%)1.03
結果:送らない人の人数7526
判断:続ける・直す・止める・まだ判断しない続ける

表に載せていないメモの列には、次に書く誤送信の日・通・初回購入の期間と、どう数えたかを書きました。

送らない人に誤って送っていないかは、送った記録の表から、日本の日付と通ごとに人数を出して確かめます。0行なら誤送信はありません。実行する前に、自社の送った記録の表で、送った日時(sent_at)の型が TIMESTAMP か DATETIME かを確かめ、SQL の先頭のコメントに従います。DATETIME の表にこのまま実行するとエラーで止まります。反対に、TIMESTAMP の表で日本時間に直さずに日付を出すとエラーにはならず、日本時間の朝8時に送った1通が前の日に数えられました。

-- 送らない人に届いた記録を、日本の日付と通ごとに数える。0行なら誤送信なし。
-- sent_at が TIMESTAMP(UTC で持つ形)の前提。DATE(s.sent_at) のままだと、日本時間の0時〜9時に送った分が前の日になる。
-- sent_at が DATETIME(日本時間で入っている)の表なら、2か所の DATE(s.sent_at, 'Asia/Tokyo') を DATE(s.sent_at) に替える(DATETIME にタイムゾーンを付けるとエラーになる)。
SELECT DATE(s.sent_at, 'Asia/Tokyo') AS sent_day, s.step_no, COUNT(*) AS sends, COUNT(DISTINCT s.customer_id) AS people,
  MIN(a.first_order_date) AS first_from, MAX(a.first_order_date) AS first_to
FROM `your_project.crm.step_sends` AS s
JOIN `your_project.crm.step_assign` AS a USING (customer_id)
WHERE a.grp = 'holdout'
GROUP BY DATE(s.sent_at, 'Asia/Tokyo'), s.step_no
ORDER BY sent_day, s.step_no;

効き目を入れたデータには、確かめ方を示すため、5月12日に、初回購入が5月1日〜11日の送らない人 446人へ2通目を送った記録を入れてあります。SQL は、この日・この通・この人数を返しました。もう1つ、同意の列を足したデータでは、割合の表だけを4月1日から3割にし、送った記録は2割の分け方のまま作ってあります。これに実行すると、4月1日以降に初回購入した送らない人へ届いた通が、日と通ごとに 273行並びました。

誤って届いた人だけを送らない人から外すと、送らない人の中身が送る側とそろわなくなります。誤って届いた人も送らない人のまま数え、念のため、その期間に初回購入した人を両方から外した差も並べます。この例では +2.98 と +3.05 ポイントで、どちらも続けるでした。

ステップ配信の中身を直したときは、直した日以降に入った人から数えます。LINE のステップ配信では、設定を変えた時点ですでに配信が始まっている人には、変更前のメッセージが送られるとマニュアルにあります。ただし、条件や変更のタイミングなどで変わる場合があるとも書かれています。

LINE で送る側のオーディエンスを開始条件にしているなら、作成から180日の期限もメモに書きます。使っているオーディエンスが使えなくなると、そのステップ配信は止まるとマニュアルにあります。続けると決めたあとも送らない人を残すなら、次の確かめる日を新しい行に書きます。

試していないのは、配信ツールの画面と、金額での判断

図の数字は、配信の効き目の大きさや、開く人と買う人の重なり具合を、説明のために決めて作ったデータから出ています。差の大きさを、自社の見込みにはしないでください。

ステップ配信の全体から外したので、どの通が効いたかは分かりません。通ごとに知りたいなら、その通だけ送らない人を別に分けます。3つに分けられることまでは確かめましたが、比べてはいません。

LINE やメールの配信ツールの画面での設定と、金額での判断は試していません。割合を途中で変えたときに割合ごとに分けて比べる SQL は、動くことまでを確かめ、結果は比べていません。

載せた SQL は、表の名前と日付だけを替えた文面のまま、BigQuery(東京)で実行して確かめました。このデータ(初回購入者 約3.7万人)では、1回の実行ごとの課金の対象は小さく、ほとんどが最低課金の分でした。送らない人を決める SQL が 40 MiB、その日の一覧が 10 MiB、比べる表・判断・誤送信が 20 MiB です。

明日の初回購入者から、顧客IDで送らない人を分け始める

明日初回購入する人から、顧客IDから出した0〜99の値で、送らない人を分け始めてください。最初の確かめる日に、記録表の1行が「もともと買う人が開いているだけでは」への答えになります。

比べる表と記録表を BigQuery で毎日更新する仕組みは、当社の集計と画面づくりの支援で一緒に作れます。

データ分析・データ基盤支援

Snowflake・BigQuery・Treasure Dataといったデータ基盤の選定から、基幹・SaaS・広告・POSに散らばったデータの集約、dbtでの指標統一、Lookerなどでのダッシュボード化までを支援します。

AT
aurant technologies 編集

上場企業からスタートアップまで、数多くのデータ分析基盤構築・AI導入プロジェクトを主導。単なる技術提供にとどまらず、MA/CRM(Salesforce, Hubspot, kintone, LINE)導入によるマーケティング最適化やバックオフィス業務の自動化など、常に「事業数値(売上・利益)」に直結する改善実績多数。

この記事が役に立ったらシェア:

導入・外注のご相談

進め方と、費用が何で決まるかをお伝えします。

費用感だけのご相談でも構いません。フォームで相談する費用の考え方を見る