はじめに
Google広告の検索語句レポートを開くと、「実際にどんな言葉で検索した人が広告をクリックしたか」が一覧で出てきます。クリック数、表示回数、費用、コンバージョン数あたりが並んでいて、これだけでも除外キーワードの判断にはそれなりに使えます。
ただ、このレポートには見えないものがあります。それは「その語句で来た人が、サイトに着いたあとどう動いたか」です。すぐ離脱したのか、複数ページを見て回ったのか、カートまで進んだけど買わなかったのか。検索語句レポートのコンバージョン列は最終結果しか見せてくれないので、「クリックは来るけど成果につながらない語句」と「成果手前まで進んでいる惜しい語句」が同じ「CV 0件」として並んでしまいます。
この2つは、打ち手がまったく違います。前者は除外したほうがいい語句かもしれませんが、後者はランディングページの改善やオファーの見直しで化ける語句かもしれません。検索語句レポートのなかだけを眺めていると、この差が見えないのです。
そこでこの記事では、Google広告の検索語句データとGA4の行動データ(直帰やCVだけでなく、エンゲージメント時間や閲覧ページ数)をBigQuery上で突合し、語句の「質」を切り分ける方法を、SQLとともに解説します。広告データとGA4をBigQueryで結合する考え方の土台は、BigQueryでGoogle広告×GA4のキーワード別ROASを計算する記事とも共通しているので、あわせて読むと理解が早いはずです。
なぜ検索語句レポートだけでは足りないのか
検索語句レポートが持っているのは、基本的に「広告媒体側の数字」です。クリックされるまではGoogleの世界の話で、クリックされた後にユーザーが自社サイトでどう振る舞ったかは、Google広告の管理画面には含まれません。
一方でGA4は、サイトに着いた後の行動を細かく記録しています。どのページに着地して、どれくらい滞在して、何ページ見て、購入イベントが発生したかどうか。つまり「クリック前」と「クリック後」で、データの持ち主が分かれているわけです。
この2つを別々に眺めていると、判断はどうしても粗くなります。たとえば次のような語句があったとします。
- 語句A: クリック30件・CV 0件・直帰率85%
- 語句B: クリック30件・CV 0件・直帰率20%・平均3ページ閲覧
検索語句レポートだけだと、AもBも「クリックはあるがCVゼロ」で同じ扱いです。でも行動データを加えると、Aは「来た人がほぼ即離脱している=意図が合っていない可能性が高い」、Bは「しっかり回遊しているのに最後の一歩が出ていない=LPやオファーの問題かもしれない」と、まったく別の仮説が立ちます。
この切り分けをするには、検索語句とGA4の行動を「同じ行」に並べる必要があります。それをやるのがBigQueryでの突合です。
突合の全体像
やることはシンプルで、大きく3つのテーブルを用意して結合します。
- Google広告の検索語句データ(検索語句・クリック・費用など)
- GA4のセッション単位の行動データ(着地ページ・直帰・エンゲージメント・購入)
- 両者をつなぐキー(多くの場合は GCLID か、UTMパラメータと着地URL)
ここでいちばん悩ましいのが「どのキーで結合するか」です。理想は GCLID(Google Click Identifier)です。GCLIDはクリック1回ごとに振られる識別子で、これがGA4側のイベントにも残っていれば、検索語句とサイト行動を1クリック単位で正確につなげます。Google広告のデータをBigQueryに取り込む方法は、Google広告データをBigQuery Data Transferで連携する設定で扱っているので、まだ取り込んでいない場合はそちらから始めてください。
GCLIDがGA4側に揃わない場合は、UTMパラメータ(utm_term など)や着地ページURL、日付の組み合わせで近似的に結合することになります。この近似がどこまで使えるかは後半の「突合の注意」で改めて触れます。
GA4側のセッション行動を整える
まずGA4のBigQueryエクスポート(events_* テーブル)から、セッション単位の行動指標を作ります。GA4はイベント単位のデータなので、session_id でまとめてセッションの粒度に落とします。
-- GA4のイベントテーブルから、セッション単位の行動指標を作る
WITH ga4_sessions AS (
SELECT
-- セッションを一意に特定するキー(ユーザー疑似ID + セッションID)
user_pseudo_id,
(SELECT value.int_value
FROM UNNEST(event_params)
WHERE key = 'ga_session_id') AS session_id,
-- GCLID(取得できている場合のみ。なければ NULL)
-- gclid は event_params ではなく collected_traffic_source.gclid に入る
MAX(collected_traffic_source.gclid) AS gclid,
-- このセッションで見たページ数
COUNTIF(event_name = 'page_view') AS page_views,
-- エンゲージメント時間(ミリ秒)を秒に換算。SUM するとイベントごとの
-- 加算で過大計上になりやすいので、ここでは MAX で代表値を取る
MAX((SELECT value.int_value
FROM UNNEST(event_params)
WHERE key = 'engagement_time_msec')) / 1000 AS engagement_seconds,
-- 購入が起きたか
COUNTIF(event_name = 'purchase') AS purchases,
-- 購入金額
SUM(IF(event_name = 'purchase', ecommerce.purchase_revenue, 0)) AS revenue
FROM `your_project.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260601' AND '20260630'
GROUP BY user_pseudo_id, session_id
)
SELECT
*,
-- 1ページしか見ずエンゲージメントも短いセッションを「直帰相当」とみなす
IF(page_views <= 1 AND engagement_seconds < 10, 1, 0) AS is_bounce
FROM ga4_sessions
ひとつ注意したいのが GCLID の取り出し方です。GCLID は event_params の中ではなく、各イベントの collected_traffic_source.gclid(セッション起点でみたい場合は session_traffic_source_last_click.manual_campaign.gclid など)に格納されます。event_params の key として gclid を探しても取れないので注意してください。なお、GA4 BigQueryエクスポートのスキーマはGA4のバージョンや設定で変わることがあるため、実際のフィールド名は必ず最新の公式ドキュメントで確認してください。
エンゲージメント時間も扱いに癖があります。engagement_time_msec はイベントごとに記録されるため、セッション内で単純に SUM するとエンゲージメント時間を過大計上しがちです。ここでは MAX で代表値を取っていますが、これも厳密な「セッション総エンゲージメント時間」ではなく簡易指標だと割り切ってください。
その上で is_bounce を自前で定義しているのは、GA4には旧UAのような単純な「直帰率」がないからです。本来 GA4 で離脱に近いのは「エンゲージメントのなかったセッションの割合(session_engaged などのフラグ)」ですが、突合分析では「何をもって離脱とみなすか」を自分で握っておいたほうが、後の解釈がブレません。ここでは「1ページしか見ず、かつエンゲージメント時間が10秒未満」を直帰相当とする簡易定義にしています。GA4の厳密な定義そのものではない点に留意しつつ、閾値は扱う商材に合わせて調整してください。
検索語句と結合する
次に、Google広告の検索語句データと先ほどのセッションデータを結合します。ここでは、上のクエリの結果を your_project.mart.ga4_sessions という中間テーブル(ビューでも可)として一度マテリアライズしておき、それを参照する前提にしています。1本のクエリにまとめても動きますが、セッション集計は使い回す場面が多いので、中間テーブルに切り出しておくと後段の分析クエリがすっきりします。以下はGCLIDが両側に揃っている前提のSQLです。
-- 検索語句データ(広告側)とGA4セッション行動を GCLID で結合する
WITH ga4_sessions AS (
-- 上のクエリで作ったセッション行動(説明のため再掲・省略可)
SELECT gclid, page_views, engagement_seconds, purchases, revenue, is_bounce
FROM `your_project.mart.ga4_sessions`
WHERE gclid IS NOT NULL
),
ads_terms AS (
SELECT
search_term, -- 実際に検索された語句
gclid, -- クリック識別子
cost_micros / 1000000 AS cost -- 費用(マイクロ単位 → 円)
FROM `your_project.google_ads.search_term_with_gclid`
WHERE segments_date BETWEEN '2026-06-01' AND '2026-06-30'
)
SELECT
a.search_term,
COUNT(*) AS clicks, -- クリック数
ROUND(SUM(a.cost)) AS cost, -- 合計費用
ROUND(AVG(g.page_views), 1) AS avg_page_views, -- 平均閲覧ページ数
ROUND(AVG(g.engagement_seconds)) AS avg_engage_sec, -- 平均エンゲージメント秒
ROUND(AVG(g.is_bounce) * 100, 1) AS bounce_rate, -- 直帰相当の割合(%)
SUM(g.purchases) AS purchases, -- 購入回数
ROUND(SUM(g.revenue)) AS revenue -- 売上
FROM ads_terms AS a
LEFT JOIN ga4_sessions AS g
USING (gclid)
GROUP BY a.search_term
HAVING clicks >= 5 -- データ量が少なすぎる語句はノイズになるので除外
ORDER BY cost DESC
これで、検索語句ごとに「費用」と「サイトでの行動」が1行に並びます。HAVING clicks >= 5 で、クリックが数件しかない語句は最初から外しています。サンプルが少ない語句は、たまたまの離脱や購入で数字が大きく振れるためです。
質の高い語句/低い語句を見つける
ここからが本題で、上のテーブルを使って語句を仕分けます。判断の軸は「費用に見合った行動をしているか」です。代表的なパターンを2つ、SQLで切り出してみます。
費用は出ているのに即離脱されている語句
まず「お金を使っているのに、ほぼ即離脱されている」語句です。除外キーワードの最有力候補になります。
-- 費用が出ているのに直帰相当が多く、購入もない語句を抽出
SELECT
search_term,
clicks,
cost,
bounce_rate,
avg_engage_sec,
purchases
FROM `your_project.mart.search_term_quality`
WHERE cost >= 3000 -- ある程度費用が出ている
AND bounce_rate >= 70 -- 7割以上が即離脱
AND purchases = 0 -- 購入ゼロ
ORDER BY cost DESC
ここに出てくる語句は、「クリックは取れているが、来た人の意図と着地ページが噛み合っていない」可能性が高いものです。意図がそもそもズレている(自社が売っていないものを探している、など)なら除外、意図は合っているのに着地が悪いなら着地ページの見直し、という分岐になります。
行動は良いのに購入に届いていない惜しい語句
逆に、「しっかり回遊しているのに、最後の購入だけ出ていない」語句です。除外してはいけない、むしろ伸ばす候補です。
-- よく回遊・滞在しているのに購入だけ出ていない「惜しい」語句
SELECT
search_term,
clicks,
cost,
avg_page_views,
avg_engage_sec,
bounce_rate,
purchases
FROM `your_project.mart.search_term_quality`
WHERE avg_page_views >= 3 -- 平均3ページ以上見ている
AND avg_engage_sec >= 60 -- 平均1分以上滞在
AND bounce_rate <= 30 -- 即離脱は少ない
AND purchases = 0 -- でも購入はゼロ
ORDER BY cost DESC
ここに並ぶ語句は、「興味はあって真剣に見ているのに、最後の一押しが足りない」状態です。LPのオファー、送料や価格の見せ方、カートまでの導線、在庫表示など、購入直前のボトルネックを疑う対象になります。検索語句レポートだけ見ていたら、どちらも「CV 0件」で一括除外していたかもしれません。この差が見えることが、突合の最大の価値です。
除外キーワードや入札への活かし方
仕分けができたら、実際の運用に落とします。ここは管理画面側の操作になりますが、判断の根拠がBigQueryのデータで裏打ちされている点が今までと違います。
- 即離脱が多く購入ゼロの語句: 除外キーワードに追加する。意図がズレている語句を消すことで、無駄クリックの費用を削れます。
- 回遊は良いが購入ゼロの語句: 除外せず、着地ページやオファーの改善対象にする。広告側ではなくサイト側の課題として扱います。
- 行動も購入も良い語句: 単独のキーワードとして切り出し、入札を強める、または専用の広告グループに分けて訴求を最適化する候補にします。
ポイントは、「除外する/しない」を直帰や購入の最終結果だけで決めず、エンゲージメントや閲覧ページ数という途中の行動も見て決めることです。途中の行動が良い語句を安易に除外すると、本当は伸ばせたはずの需要を自分で切り捨てることになりかねません。
この仕分けは一度やって終わりではなく、月次で回すのが現実的です。語句のトレンドは季節やセールでも動くので、定点観測して「先月は惜しかった語句が、LP改善後にどう変わったか」を追えると、改善の手応えが数字で見えるようになります。
突合の注意点(語句マッチの限界)
最後に、この分析を過信しないための注意を書いておきます。突合分析は強力ですが、結合のキーまわりに弱点があります。
GCLIDがGA4側に揃わないことがある。サイトのタグ設定や同意管理(コンセントモード)、ユーザーのブラウザ設定によっては、GCLIDがGA4のイベントに残らないケースがあります。揃わなければ LEFT JOIN の結果は NULL になり、その分のセッションは行動指標が欠けます。突合できた割合(GCLIDがマッチした率)を必ず確認し、低い場合は結論を控えめに扱ってください。
GCLIDがない場合の近似結合は精度が落ちる。UTMパラメータや着地URL+日付で近似的に結合する場合、同じ語句・同じ着地ページで複数の人が来ていると、誰の行動か正確には紐づけられません。傾向をつかむには使えますが、語句ごとの厳密な数字としては読まないほうが安全です。
検索語句とキーワードは別物。検索語句レポートに出るのは「実際に検索された語句」で、自分が登録した「キーワード」とは違います。部分一致やフレーズ一致を使っていると、1つのキーワードに対して多種多様な検索語句が紐づきます。さらに、Googleはプライバシー保護のため、検索ボリュームが少ない語句を検索語句レポートに出さないことがあります。つまり「レポートに出ている語句がすべてではない」ので、表示されていない語句にも費用が使われている前提で見る必要があります。
サンプル数の少なさに注意。クリックが数件しかない語句は、1件の離脱や購入で数字が極端に振れます。今回 HAVING clicks >= 5 で足切りしていますが、判断するならもっと多いクリック数を基準にしたほうが安全です。
これらは「だからやらない」という話ではなく、「どこまで信じていいかの線引き」をしておこう、という話です。突合データは管理画面の数字を補強する強力な物差しですが、唯一の正解ではありません。
まとめ
検索語句レポートは「クリックされるまで」の情報しか持たず、GA4は「クリックされた後」の情報を持っています。この2つをBigQueryで突合すると、これまで「CV 0件」と一括りにされていた語句を、「即離脱されている語句」と「惜しいところまで進んでいる語句」に切り分けられます。
- 即離脱が多く購入ゼロの語句は、除外キーワードの候補
- 回遊・滞在は良いのに購入ゼロの語句は、LPやオファーの改善候補
- どちらも検索語句レポート単体では見分けがつかない
結合キーはGCLIDが理想で、揃わない場合は近似結合になり精度が落ちること、検索語句とキーワードは別物でレポートに出ない語句もあること――この前提さえ押さえておけば、広告の「無駄」と「伸びしろ」をデータで切り分けられるようになります。GA4とBigQueryの突合という考え方は広告以外にも応用が利くので、自然検索側の流入を分析するGA4・BigQuery・Search Consoleでオーガニック流入を分析する記事もあわせて読むと、流入チャネル全体を同じ目線で見られるようになります。