
このSQLで解決できる課題
LPや商品詳細、料金ページの改善では、ユーザーが“次に”どこへ進んだかを把握するのが近道です。
GA4の標準レポートでもナビゲーション概要は見られますが、自社のKPIに合わせた粒度(特定ページのみ・重複除外・URL正規化・セッション母数に対する遷移率など)まで落とし込むのは難しく、改善会議で「どの導線を強化/削除するか」を即断できないことが多いはず。
本SQLは、GA4の events_* から page_view だけを抽出し、同一ユーザー×セッション内の時系列で LEAD() を使って**“次ページ”を決定。
対象の起点ページ(YOUR_TRIGGER_PAGE)を訪れた全セッションの母数と、そこから実際に別ページへ遷移したセッション数を遷移先ごと**に集計します。
出力は、起点パス / 遷移先パス / 遷移セッション数 / 起点母数 / 遷移率。
これにより「LPからそのままカートに行った割合」「FAQへ逸れてしまう割合」「別商品の詳細に流れてしまう割合」を数字で比較でき、CTA配置の見直し、FAQ要点のLP内包、無駄な外部リンク削減など、施策に直結する意思決定が可能になります。
実行結果イメージ
-
背景・利用シーン
LP改善の優先順位付け:カート・フォームへの直行が低いなら、ファーストビューやCTA配置の見直し。
FAQ/ヘルプ流入の最適化:FAQ遷移が多いなら、LP側に要点を前倒しして**“行かなくても済むFAQ”**へ。
商品回遊の導線設計:似た商品へ流れるなら、比較表やおすすめの並びを改善。
広告整合性の検証:意図した次ステップに進んでいるか、キーワード/クリエイティブの整合性を確認。
抽出結果からできる議論(会話例)
マーケ担当A: 「LPからカートに直行するのは24%しかないですね。」
分析担当B: 「FAQに10%流れているのが気になります。LPに情報を追記するべきかも。」
マーケ担当A: 「この商品ページに誘導したいのに、別商品の詳細に流れてますね。」
分析担当B: 「一覧ページでの見せ方を変える必要がありそうです。」
分析担当B: 「コンタクトフォームに進む割合が6%しかないですね。」
マーケ担当A: 「ボタン配置を見直したほうが良さそうです。」
マーケ担当A: 「広告経由のユーザーは、意外と商品詳細よりFAQに行ってますね。」
分析担当B: 「LPの説得力が弱い証拠です。改善ポイントが見えてきました。」
マーケ担当A: 「遷移率が高い導線は、そのまま強化したいですね。」
分析担当B: 「逆に低い導線は、思い切って削除するのもアリです。」
SQLコードと解説
-- ============================================================
-- 🚀 ページA → 次ページBの遷移率(セッション単位)
-- 手動入力は「★1:期間」「★2:起点ページ」「★3:テーブルパス」だけ!
-- ============================================================
-- ▼ ★1:解析期間を手動で指定(YYYY-MM-DD 形式)
DECLARE start_date STRING DEFAULT '2025-10-01'; -- 解析開始日
DECLARE end_date STRING DEFAULT '2025-10-・・'; -- 解析終了日
-- ▼ ★2:起点ページの正規表現(正規化後のパスにマッチさせる)
-- 例)'^/contact$'、'^/cars/[^/]+/detail$'、'^/campaign'
DECLARE trigger_path_pattern STRING DEFAULT '^/$'; -- トップページの場合はこれでOK
-- ▼ 内部用(編集不要)
DECLARE sfx_start STRING;
DECLARE sfx_end STRING;
SET sfx_start = REPLACE(start_date, '-', '');
SET sfx_end = REPLACE(end_date, '-', '');
-- ▼ ★3:参照テーブルパス(あなたの環境に一度だけ置換)
-- 例)`myproject.analytics_123456789.events_*`
-- ============================================================
-- ① page_viewのみ抽出しURLを正規化(小文字化・ドメイン除去・クエリ除去・末尾スラ削除)
WITH base_events AS (
SELECT
user_pseudo_id,
(SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS ga_session_id,
event_timestamp,
-- 元URL(確認用)
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page_location_raw,
-- 正規化処理(ドメイン削除 → クエリ削除 → 末尾スラ吸収)
IF(p != '/' AND RIGHT(p, 1) = '/', SUBSTR(p, 1, LENGTH(p) - 1), p) AS page_path
FROM (
SELECT
user_pseudo_id,
event_timestamp,
event_params,
REGEXP_REPLACE(
REGEXP_REPLACE(
LOWER((SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location')),
r'^https?://[^/]+', '' -- ドメイン除去
),
r'[\?#].*$', '' -- クエリ・アンカー除去
) AS p,
(SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS ga_session_id
FROM `・・・.analytics_・・・.events_*` -- ★3:ここを環境に合わせて置換
WHERE event_name = 'page_view'
AND _TABLE_SUFFIX BETWEEN sfx_start AND sfx_end
)
WHERE ga_session_id IS NOT NULL
),
-- ② 同一セッション内の次ページを特定
page_views AS (
SELECT
user_pseudo_id,
ga_session_id,
event_timestamp,
page_path,
LEAD(page_path) OVER (
PARTITION BY user_pseudo_id, ga_session_id
ORDER BY event_timestamp
) AS next_path
FROM base_events
),
-- ③ 起点ページを踏んだセッション(母数)
trigger_sessions AS (
SELECT DISTINCT
user_pseudo_id,
ga_session_id
FROM page_views
WHERE REGEXP_CONTAINS(page_path, trigger_path_pattern)
),
-- ④ 有効な遷移(直後に別ページへ遷移)
valid_transitions AS (
SELECT DISTINCT
pv.user_pseudo_id,
pv.ga_session_id,
pv.page_path AS trigger_page_path,
pv.next_path AS target_page_path
FROM page_views pv
JOIN trigger_sessions ts
ON pv.user_pseudo_id = ts.user_pseudo_id
AND pv.ga_session_id = ts.ga_session_id
WHERE REGEXP_CONTAINS(pv.page_path, trigger_path_pattern)
AND pv.next_path IS NOT NULL
AND pv.page_path != pv.next_path
),
-- ⑤ 遷移先ごとのセッション数
transition_counts AS (
SELECT
trigger_page_path,
target_page_path,
COUNT(DISTINCT ga_session_id) AS transitioned_sessions
FROM valid_transitions
GROUP BY trigger_page_path, target_page_path
),
-- ⑥ 起点ページを踏んだユニークセッション数(母数)
trigger_base AS (
SELECT COUNT(DISTINCT ga_session_id) AS total_trigger_sessions
FROM trigger_sessions
)
-- ⑦ 出力(母数と遷移率)
SELECT
tc.trigger_page_path,
tc.target_page_path,
tc.transitioned_sessions,
tb.total_trigger_sessions,
SAFE_DIVIDE(tc.transitioned_sessions, tb.total_trigger_sessions) AS transition_rate
FROM transition_counts tc
CROSS JOIN trigger_base tb
ORDER BY tc.transitioned_sessions DESC, tc.target_page_path
LIMIT 100;
確認すべきポイント
- 対象の起点指定
YOUR_TRIGGER_PAGEをパスで指定(例:/lp/campaign2025)。クエリ差異を吸収したい場合は、前段でクエリ除去+小文字化(REGEXP_EXTRACT(LOWER(page_location), r'^https?://[^/]+([^?]+)'))を推奨。 期間とタイムゾーン
期間フィルタが無いので、必要ならevent_date由来の_TABLE_SUFFIX条件を追加。Asia/Tokyo集計にするならTIMESTAMP→DATETIME変換で日付境界を合わせる。セッション粒度の揺れ
ga_session_idの生成はGA4側ロジック依存。Consentやクロスデバイスでセッション分断が起きる点は前提として共有。外部/内部リンクの扱い
外部遷移は次ページがNULLになりやすい(サイト外ログが無い)。内部導線評価に使う前提でOK。ページ階層の統合
/product/abc/?ref=...など表記ゆれは事前正規化で吸収。末尾スラッシュや大文字小文字も統一を。ランキングの読み方
遷移率が高い=必ず良いではない。FAQ・利用規約への高遷移は「LP不足」を示唆する場合がある。文脈で解釈を。深掘り拡張
“次ページ”ではなく**“n手先”**(LEAD(..., n))を見る、流入別(traffic_source.medium)でセグメントするなど拡張可能。
- 対象の起点指定
まとめ
このSQLを使うと、「特定ページからユーザーがどこへ進むのか」 を明確に把握できる。
起点ページからの“次の一歩”を、セッション集合の母数と並べて遷移率で比較できる。
LPや商品詳細、価格ページで強い導線/弱い導線がはっきりするため、CTA配置・FAQ内包・関連リンクの改善に直結。
URL正規化・期間/TZ・セッション粒度を押さえれば、毎週の改善会議で回せる定例レポートのコアになる。




















