Search Console×BigQueryの使い方|SQLとAIでSEOデータを分析する方法

第1回で、Search Console(サーチコンソール)の一括データエクスポートを設定し、BigQueryに3つのテーブルが生成されるところまでを確認しました。ここで多くの人が止まります。「テーブルはできた。で、何を見ればいいのか」——BigQueryを開いても、何を書けばいいか分からなければ、管理画面より不便なだけです。
この記事では、SEGO(当サイト)のBigQueryに実際に蓄積されたSearch Consoleのデータを使って、日別の推移 → 検索クエリ → URL別の成果 → 「表示回数は多いのにCTRが低いページ」の抽出までを、実行したSQLと結果をそのまま見せながら解説します。ゴールは「BigQuery上のデータ構造を理解し、基本のSQLを実行し、SEO改善候補を抽出できる」状態です。
あわせて、SQLを書いたことがない人がChatGPTやClaudeなどの生成AIをどう補助として使うかも扱います。今回、筆者がAIに作らせたSQLは実際にBigQueryでエラーになりました。その原因と直し方も実例ベースで紹介します。
なお、まだBigQueryへの一括データエクスポート設定が終わっていない場合は、先に第1回のSearch ConsoleとBigQueryの連携方法を済ませてください。本記事では設定手順は繰り返しません。
この記事でわかること
- BigQueryに入るSearch Consoleの3つのテーブルと使い分け
- 分析前に必ず押さえる3つの基本ルール(集計・期間・検索タイプ)
- 匿名化クエリがBigQuery上でどう扱われているか
- 日別・検索クエリ別・URL別に集計するコピペ用SQL
- 「表示回数は多いのにCTRが低いページ」を抽出するSQL
- SQL初心者がChatGPT / Claudeを補助として使う方法と、エラーが出たときの対処
本記事の仕様は2026年9月時点のGoogle公式情報(Search Consoleヘルプ「テーブルのガイドラインとリファレンス」、同「クエリのガイドラインとサンプルクエリ」、BigQueryの料金)をもとにしています。SQLの例は、プロジェクトIDとデータセットIDをダミー値(YOUR_PROJECT.searchconsole_yoursite)にしてあるので、自分の環境に置き換えて実行してください。
Contents
Search Console×BigQueryで何を分析できる?
Search Consoleの管理画面でも、検索クエリやページごとの表示回数・クリック数は見られます。それをわざわざBigQueryに移す意味は、「Googleが用意した切り口」ではなく「自分の条件」でデータを取り出せるようになることです。
具体的には、次のようなことができます。
- クエリを独自条件で抽出する:「表示回数50以上、かつ平均順位が5位〜15位のクエリだけ」のような条件で絞る
- URLを条件で抽出する:「表示回数は多いのにCTRが低いURL」を一覧にする(本記事の分析4)
- 長期データを蓄積する:管理画面の16か月・1,000行の制限に縛られず、設定日以降のデータを貯め続けられる
- SQLで集計を自動化する:一度書いたSQLは、期間を変えるだけで何度でも同じ集計ができる
- GA4など他のデータと同じ基盤に置く:検索前(Search Console)と流入後(GA4)のデータを同じBigQueryで扱える
ただし、誤解しやすい点を先に書いておきます。Search Consoleのデータは、検索クエリやURLごとの「集計値」です。「この検索クエリを入力した特定の人が、その後サイトでコンバージョンした」という1対1の追跡はできません。GA4とのつなぎ方は後半で改めて説明します。
まず3つのテーブルを理解する
一括データエクスポートを設定すると、BigQueryのデータセットに3つのテーブルが作られます。SEGOでは searchconsole_sego というデータセットに、次の3つが生成されています。
Google公式のテーブルリファレンスをもとに、3つの役割を整理すると次の通りです。
| テーブル | 何が入っているか | 使う場面 |
|---|---|---|
ExportLog | エクスポートが成功した記録。どの日付(data_date)のデータが、いつ(publish_time)どのテーブルに保存されたか。失敗したエクスポートは記録されません | 「データがちゃんと入っているか」の確認 |
searchdata_site_impression | プロパティ(サイト)単位で集計された検索パフォーマンス。日付・検索クエリ・国・検索タイプ・デバイスごとの表示回数・クリック数・掲載順位の合計 | 検索クエリ全体の分析、サイト全体の推移 |
searchdata_url_impression | URL単位で集計された検索パフォーマンス。上の項目に加えて、着地URL(url)と、リッチリザルトなど検索結果での見え方を示す列が入る | URL別の成果、URL×クエリの組み合わせ分析 |
初心者が迷いやすいのは「site_impression と url_impression のどちらを使うか」です。目安は次の通りです。
- 検索クエリ全体を見たい(どんな言葉で表示されているか)→
searchdata_site_impression - URL単位まで見たい(どのページが成果を出しているか、どのページを直すか)→
searchdata_url_impression - エクスポートが動いているか確認したい →
ExportLog
なお、同じ「表示回数」でも、site_impression と url_impression では数え方が異なります。site_impression はプロパティ単位なので、1回の検索で同じサイトのURLが2つ表示されても表示回数は1です。url_impression はURL単位なので、この場合は2つのURLにそれぞれ1回ずつ表示回数が付きます。両者の合計が一致しなくても異常ではありません。
BigQueryでGSCを分析するときの3つの基本ルール
SQLを書く前に、Search Consoleのデータ特有の3つのルールを押さえてください。ここを飛ばすと、集計値がずれたり、無駄に処理量が増えたりします。
1. 必ずSUMなどで集計する
Google公式のガイドラインには、「テーブルのデータは、日付・URL・サイトなどのキーで1行にまとめられている保証はない」と明記されています。Search Consoleはデータを少しずつ追加していくため、同じ日付・同じURL・同じクエリの組み合わせが複数行に分かれて入っていることがあります。
そのため、公式は「常にSUMやCOUNTなどで集計すること」を推奨しています。「1行=1つのクエリ」だと思ってそのまま読むと、数字が分かれたままになります。本記事のSQLは、すべてSUMで集計してからGROUP BYでまとめる形にしています。
2. data_dateで期間を絞る
3つのテーブルは、いずれもdata_date(データの日付)でパーティション分割されています。パーティションとは、テーブルを日付ごとの区画に分けて保存する仕組みです。WHERE句で data_date の範囲を指定すると、BigQueryは該当する日付の区画だけを読みに行くので、処理するデータ量(bytes processed)が減ります。
BigQueryのオンデマンド料金は「クエリが処理したバイト数」で決まるため、期間を絞ることはそのまま料金対策になります。Google公式のクエリガイドラインでも「WHERE句で日付範囲を絞ることがコストを抑える良い方法」と案内されています。本記事のSQLは、すべて data_date で期間を指定しています。
3. search_typeを必要に応じて指定する
テーブルには、通常のウェブ検索だけでなく、画像検索・動画検索・ニュース・Discoverなど、検索タイプ(search_type)の異なるデータが混ざって入っています。ウェブ検索の成果を見たいのに画像検索の表示回数が混ざると、CTRや順位の解釈がずれます。
本記事の検索クエリ分析・URL分析では、Google公式のサンプルクエリにならって search_type = 'WEB' でウェブ検索に絞っています。SEGOの実データでも、値は 'WEB'・'IMAGE' のように大文字で入っていました。
匿名化クエリを理解する
検索クエリを集計する前に、匿名化クエリ(anonymized query)を知っておく必要があります。Search Consoleは、ユーザーのプライバシー保護のため、検索された回数がごく少ないクエリなどを「匿名化クエリ」として扱い、検索語を表示しません。管理画面のクエリ一覧に出てこないクエリがあるのはこのためです。
BigQueryのテーブルでは、この匿名化クエリを is_anonymized_query という列で判別できます。SEGOの searchdata_url_impression をプレビューした画面が次の画像です。
ここで、公式仕様とSEGOで観測した内容を分けて整理します。
- 公式仕様:テーブルリファレンスでは、匿名化クエリは is_anonymized_query が true になり、query 列は「長さ0の文字列」または「null」になる、と説明されています(同じリファレンス内で2通りの表現が使われています)。検索語そのものは、プライバシー保護のため取得できません。
- SEGOで観測した内容:query 列は null で、is_anonymized_query は true でした。一方、url・country・search_type・clicks・impressions などの他の列は、通常の行と同じように値が入っていました。
つまり、「どんな言葉で検索されたか」は分からないが、「そのURLが匿名化クエリ経由で何回表示・クリックされたか」の集計値は残っている、というのがBigQuery上での匿名化クエリの姿です。検索語を無理に推測するのではなく、「匿名化分」としてまとめて扱うのが正しい使い方です。
管理画面との数値のずれについても補足します。Search Consoleヘルプによると、管理画面のクエリ一覧では匿名化クエリが表から省かれますが、グラフの合計値には含まれます。BigQueryでも同様に、クエリ別に集計するときに匿名化クエリを除外すれば、その合計は全体の合計より小さくなります。公式は「もっとも多いクエリを見たいなら、空のクエリ値を除外することになるだろう」と案内しているので、クエリ別の分析では除外し、サイト全体の合計を見るときは含める、と使い分けてください。
分析1|日別のクリック数・表示回数を確認する
最初に実行するSQLは、日別のクリック数・表示回数・行数の集計です。分析というより「どの日付のデータが、どれくらい入っているか」を確かめるためのものです。ここで期待通りの日付が出てこなければ、エクスポートの設定や反映待ちを疑います。
SELECT
data_date,
SUM(clicks) AS total_clicks,
SUM(impressions) AS total_impressions,
COUNT(*) AS row_count
FROM
`YOUR_PROJECT.searchconsole_yoursite.searchdata_url_impression`
WHERE
data_date BETWEEN '2026-08-28' AND '2026-09-01'
GROUP BY
data_date
ORDER BY
data_date;
SQLの意味を1行ずつ説明すると、次の通りです。
- SELECT:取り出す列。日付と、クリック数の合計、表示回数の合計、行数
- FROM:どのテーブルから取るか。ここではURL単位のテーブル
- WHERE:期間の指定。この2つの日付を自分の見たい期間に変えて使ってください
- GROUP BY:日付ごとにまとめる
- ORDER BY:日付順に並べる
SEGOで2026年8月28日〜9月1日の5日間を対象に実行した結果が次の画像です。
この5日間のSEGOの実測値は、次の通りでした。
| data_date | クリック数 | 表示回数 | 行数 |
|---|---|---|---|
| 2026-08-28 | 7 | 357 | 178 |
| 2026-08-29 | 2 | 355 | 197 |
| 2026-08-30 | 10 | 663 | 243 |
| 2026-08-31 | 14 | 633 | 242 |
| 2026-09-01 | 11 | 612 | 233 |
| 5日間合計 | 44 | 2,620 | 1,093 |
行数(row_count)を一緒に出しているのには理由があります。1日あたり200行前後の行が入っている、という規模感が分かると、「1,000行の壁」に阻まれていた管理画面との違いを実感できますし、後述する料金の見当も付きます。実際、この5日分の集計で処理されたデータ量は約25KBで、BigQueryの無料枠(毎月1TiB)に対して無視できる量でした。
なお、このSQLでは search_type を絞っていないので、画像検索なども含んだ全体の数字です。ウェブ検索だけを見たい場合は、WHERE句に AND search_type = 'WEB' を足してください。
分析2|検索クエリ上位を抽出する
次に、どんな検索語で表示・クリックされているかを見ます。クエリ全体の分析なので、テーブルは searchdata_site_impression を使います。
SELECT
query,
SUM(clicks) AS total_clicks,
SUM(impressions) AS total_impressions,
SAFE_DIVIDE(SUM(clicks), SUM(impressions)) AS ctr,
SAFE_DIVIDE(SUM(sum_top_position), SUM(impressions)) + 1 AS avg_position
FROM
`YOUR_PROJECT.searchconsole_yoursite.searchdata_site_impression`
WHERE
data_date BETWEEN '2026-08-28' AND '2026-09-01'
AND search_type = 'WEB'
AND is_anonymized_query = FALSE
GROUP BY
query
ORDER BY
total_clicks DESC,
total_impressions DESC
LIMIT 20;
新しく出てきた要素を説明します。
- SAFE_DIVIDE(a, b):a ÷ b を計算する関数。普通の割り算(a / b)と違い、b が0のときにエラーにならず null を返します。表示回数が0の行があっても止まらないので、CTRの計算はこれを使うのが安全です
- ctr:クリック数 ÷ 表示回数。結果は割合なので、0.25なら25%です。パーセント表示にしたければ、末尾に
* 100を付けてください - avg_position:平均掲載順位。site_impression では sum_top_position を使います。この値は「0が1位」の0ベースで入っているため、Google公式の計算式のとおり、表示回数で割ったあとに +1 して1ベース(1が1位)に直しています
- is_anonymized_query = FALSE:匿名化クエリを除外する条件。Google公式のサンプルは
query != ''(空文字を除外)で書かれていますが、SEGOの実データでは query が null だったため、判定用の列で除外する方が確実です - LIMIT 20:上位20件だけ表示する。増やしても構いません
SEGOでの実行結果が次の画像です。
この5日間では、サイト名の「sego」がクリック数1位で、CTRは約26%でした。次いで「json-ld」が表示回数127・クリック3・平均順位4.4位、「faq 構造化データ」「json-ld 書き方」「faq schema」など、構造化データ関連のクエリが並んでいます。「どの記事群がどんな言葉で露出しているか」がこの1本のSQLで分かるのがポイントです。
一部のクエリで平均掲載順位が「-」(null)になっている点も見ておいてください。sum_top_position が入っていない行だけのクエリでは、SUMの結果も null になります。順位が空のクエリがあっても、SQLの間違いではありません。
繰り返しになりますが、これは5日間の短期データです。SEGOの現在のSEO評価や最新の順位を示すものではなく、SQLの動きを確認するための実例として見てください。
分析3|URL別の成果を確認する
今度はURL単位です。どのページがクリックを獲得しているかを見るので、テーブルは searchdata_url_impression に変わります。
SELECT
url,
SUM(clicks) AS total_clicks,
SUM(impressions) AS total_impressions,
SAFE_DIVIDE(SUM(clicks), SUM(impressions)) AS ctr,
SAFE_DIVIDE(SUM(sum_position), SUM(impressions)) + 1 AS avg_position
FROM
`YOUR_PROJECT.searchconsole_yoursite.searchdata_url_impression`
WHERE
data_date BETWEEN '2026-08-28' AND '2026-09-01'
AND search_type = 'WEB'
GROUP BY
url
ORDER BY
total_clicks DESC,
total_impressions DESC
LIMIT 20;
分析2との違いは2点だけです。テーブルが url_impression になったことと、平均掲載順位の元になる列が sum_top_position から sum_position に変わったことです。
掲載順位の列はテーブルで異なる
searchdata_site_impression→ sum_top_position(サイトとして最上位に出た順位の合計)searchdata_url_impression→ sum_position(そのURLが出た順位の合計)
どちらも0ベース(0が1位)なので、Google公式の計算式は SUM(列) / SUM(impressions) + 1 です。テーブルと列名の組み合わせを取り違えると、列が存在しないというエラーになります。
SEGOでの実行結果が次の画像です。
この5日間では、FAQ構造化データの記事がクリック20・表示回数307・CTR約6.5%・平均順位6.7位で1位でした。分析2で構造化データ関連のクエリが上位に来ていたことと、きれいにつながります。クエリ側とURL側の両方を見ると、「どの言葉で、どのページが評価されているか」が立体的に分かるようになります。
一方で、JSON-LDの解説記事は表示回数351と最多なのにクリックは6(CTR約1.7%)、別の記事は表示回数153でクリック1、平均順位48位、という行もあります。この「表示はされているのにクリックされていない」状態を、次の分析4で機械的に抽出します。
分析4|表示回数は多いのにCTRが低いページを探す
ここが本記事でいちばん実務に直結するSQLです。「一定以上表示されているのに、CTRが低いURL」は、検索結果には出ているのにクリックされていないページです。タイトルやディスクリプション(検索結果に出る説明文)が検索意図とずれている、あるいは順位が低くて目に入っていない、といった改善候補になります。
SQLは、分析3の集計を一度「page_metrics」という名前の仮の表にまとめてから(この書き方をCTE、WITH句といいます)、条件で絞り込む2段構えにしています。集計と絞り込みを分けると、初心者でも読みやすく、集計した列に対する条件指定でエラーが出にくくなります。
WITH page_metrics AS (
SELECT
url,
SUM(clicks) AS total_clicks,
SUM(impressions) AS total_impressions,
SAFE_DIVIDE(SUM(clicks), SUM(impressions)) AS ctr,
SAFE_DIVIDE(SUM(sum_position), SUM(impressions)) + 1 AS avg_position
FROM
`YOUR_PROJECT.searchconsole_yoursite.searchdata_url_impression`
WHERE
data_date BETWEEN '2026-08-28' AND '2026-09-01'
AND search_type = 'WEB'
GROUP BY
url
)
SELECT
*
FROM
page_metrics
WHERE
total_impressions >= 50
ORDER BY
ctr ASC,
total_impressions DESC;
後半の total_impressions >= 50 が、「表示回数50以上」の条件です。この「50」はGoogleの推奨値ではなく、今回の分析のために筆者が設定した独自の値です。5日分の小さなデータなので50にしましたが、28日分なら100や300など、サイトの規模と期間に応じて変えてください。ORDER BY ctr ASC でCTRの低い順に並べています。
SEGOでの実行結果が次の画像です。
この5日間では、GEOとSEOの違いを解説した記事が表示回数370に対してクリック0、平均掲載順位は約20位でした。noindexの解説記事も表示195でクリック0、平均順位は約36位です。管理画面で1ページずつ眺めていては気づきにくい「表示はされているのに、クリックされていないページ」が、SQL1本で条件に合う順に並ぶのが、BigQueryに移した最大の価値です。
ただし、ここで注意があります。この結果だけで「すぐリライトする」と判断したわけではありません。理由は3つです。
- 5日分しかない:曜日や一時的な変動の影響が大きく、傾向として信頼するには短すぎます
- 順位が低い:平均20位や36位のページは、検索結果の2〜4ページ目に表示されています。CTRが低い主因は「クリックされないタイトル」ではなく「そもそも目に入る位置にない」可能性が高く、対策はタイトル改善ではなくコンテンツや内部リンクの強化になります
- クエリの中身を見ていない:どんな検索語で表示されているかを見ずに、ページだけを見て判断すると外します
実務では、期間を28日や3か月に広げ、平均順位の条件(たとえば「順位10位以内なのにCTRが低い」)を組み合わせ、抽出されたURLについて検索クエリと実際の検索結果画面を確認してから、リライトするかを決めます。SQLの役割は「調べる対象を絞ること」であって、「施策を決めること」ではありません。
SQLが分からなくてもChatGPTやClaudeを使える
ここまでのSQLは、コピーして日付とテーブル名を変えれば動きます。ただ、「順位の条件も足したい」「クエリとURLを組み合わせたい」と少しでも変えたくなると、SQL初心者は手が止まります。そこで役に立つのが、ChatGPTやClaudeなどの生成AIです。
ただし、「BigQueryでSearch Consoleを分析するSQLを書いて」と丸投げしても、使えるSQLは返ってきません。AIは、あなたのテーブル名も列名も知らないからです。うまくいくコツは、次の4つを一緒に渡すことです。
- 目的:何を、どんな条件で取り出したいか
- テーブル:使うテーブルのフルパス(ダミーでも構いません)
- カラム:使う列の名前。data_date、url、clicks、impressions、sum_position など
- 条件:期間の絞り方、集計の単位、search_type、出力形式
SQL作成用プロンプトの例
筆者が実際に使った形に近い、プロンプトのテンプレートです。角括弧の部分を自分の内容に置き換えて使ってください。
以下の条件でBigQuery Standard SQLを作ってください。
目的:
Search ConsoleのURL別データから、
直近28日で表示回数100以上、
CTRが低いページを抽出したい。
テーブル:
YOUR_PROJECT.searchconsole_yoursite.searchdata_url_impression
使用カラム:
data_date
url
clicks
impressions
sum_position
search_type
条件:
・search_type = 'WEB'
・data_dateで期間を絞る
・URL単位でSUMして集計
・CTRを計算
・平均掲載順位も計算(sum_positionは0ベースなので +1 する)
・BigQuery Standard SQL
・実行可能なSQLだけ先に出力
・各処理の意味も後で初心者向けに説明
ポイントは、本記事の「3つの基本ルール」(集計・期間・search_type)をそのまま条件として書いていることです。ルールを知っていれば、AIへの指示も具体的になります。逆に、ルールを知らずに「いい感じに分析して」と頼むと、集計されていないSQLや、期間指定のないSQLが返ってくることがあります。
「平均掲載順位は0ベースなので +1」のように、公式仕様に基づく注意点を書き添えておくのも効果的です。AIはSearch Consoleの列の細かい仕様を正確に覚えているとは限らないため、仕様は人間が渡すと考えてください。
AIが作ったSQLでエラーが出たらどうする?
今回の検証で、筆者がAIに作らせたSQLは、実際にBigQueryで2回エラーになりました。どちらも「AIが出したSQLだから正しい」と思っていると気づきにくいものです。
エラー1:rows という別名で構文エラー
行数を数える列に COUNT(*) AS rows という別名(alias)を付けたSQLが返ってきました。ところが、rows はBigQueryの予約語(SQLの文法で特別な意味を持つ言葉)なので、そのまま別名に使うと構文エラーになります。本記事のSQLで row_count という名前にしているのは、このためです。
エラー2:Aggregations of aggregations are not allowed
2つ目は、SUM(clicks) AS clicks のように、集計した結果に元の列と同じ名前を付け、さらにその名前を別の集計や並べ替えで参照したSQLです。BigQueryは「集計の集計はできない」というエラー(Aggregations of aggregations are not allowed)を返しました。集計後の別名は total_clicks のように元の列名と分け、絞り込みはCTE(WITH句)で集計を終えたあとに行う、というのが本記事の書き方に落ち着いた理由です。
エラーが出たときの直し方
エラーが出たら、自分で原因を探す前に、エラーメッセージの全文をそのままAIに返すのが早道です。そのとき、次の3つをセットで渡してください。
- 元のSQL:AIが出したSQLをそのまま
- エラーメッセージ:BigQueryに表示された文言を省略せずに
- テーブルのスキーマ:BigQueryのテーブル画面の「スキーマ」タブに出る列名と型の一覧。これがあると、存在しない列名を使う間違いが減ります
今回のエラー2つも、エラー文をそのまま渡すと、AIは原因(予約語、集計の二重参照)を説明したうえで修正版を返してきました。ここで大事なのは、AIはSQLを書く補助者であって、正しさを保証する存在ではないということです。修正版が返ってきても、必ずBigQueryで実行し、結果の数字が日別集計と矛盾しないかを確かめてください。最終確認は、人間とBigQueryの実行結果です。
SQL結果をAIにSEO分析させる方法
SQLで改善候補を抽出できたら、次の段階として、集計結果をAIに渡して整理させる使い方があります。分析4の結果のように、URL・クリック数・表示回数・CTR・平均順位が並んだ表を渡し、優先度の分類を手伝ってもらう形です。
BigQueryの結果画面にある「結果を保存」からCSVでダウンロードするか、必要な行だけをコピーして貼り付けます。プロンプトの例は次の通りです。
以下はSearch ConsoleのURL別集計です
(対象期間:2026-08-28〜09-01の5日間、ウェブ検索のみ)。
各URLについて、
1. 現状
2. 改善優先度(高・中・低)
3. 判断理由
4. SEO担当者が確認すべきこと
を整理してください。
ただし、表示回数やCTRだけでリライトを確定せず、
検索意図・実際の検索結果画面・ページ内容も確認する前提で
評価してください。
また、対象期間が短いことによる限界も明記してください。
[ここに集計結果を貼り付け]
プロンプトの後半で「表示回数やCTRだけで確定しない」「期間が短い限界を明記する」と釘を刺しているのは、AIが数字だけを見て「このページをリライトすべき」と断定しがちだからです。判断の前提を人間が指定すると、AIの出力は「確認すべきことのリスト」に近づき、実務で使いやすくなります。
AIに渡す前に確認すること
- APIキー・認証情報を含めない(BigQueryの接続情報やサービスアカウントのキーなど)
- プロジェクトIDなど不要な内部情報を含めない。分析に必要なのはURLと数値だけです
- 個人情報を含めない。検索クエリに氏名やメールアドレスのような文字列が含まれていないか確認する
- 社外秘のデータ(未公開ページのURL、クライアントのデータなど)を無断で渡さない
- 企業で使う場合は、社内のAI利用ルール(利用できるサービス、渡してよいデータの範囲)に従う
Search Consoleの集計値は公開されているURLと検索語の集計が中心なので、比較的リスクの低いデータです。それでも「何を渡しているか」を毎回意識する習慣は、他のデータをAIに渡すときの土台になります。
GSCとGA4をBigQueryに入れると何ができる?
Search Consoleのデータが扱えるようになると、「GA4のコンバージョンとつなげられないか」と考える人が多いはずです。できることと、できないことを分けて説明します。
2つのデータは、見ている場面が違います。
| Search Console(BigQuery) | GA4(BigQuery) | |
|---|---|---|
| 見ている場面 | 検索結果画面まで(流入前) | サイトに入ってから(流入後) |
| 主な項目 | query、impressions、clicks、position、着地URL(url) | session、page_view、CTAクリック、form_start、generate_lead などのイベント |
| データの粒度 | 日付×クエリ×URL×国×デバイスなどの集計値 | ユーザー(仮名ID)×セッション×イベントの行データ |
ここで重要なのは、「Search Consoleのあるクエリを検索した特定のユーザー」と「GA4でコンバージョンしたユーザー」を直接結び付ける共通のIDは存在しないことです。Search Consoleにはユーザー単位のIDがそもそもなく、GA4側の user_pseudo_id と突き合わせる手段はありません。「この検索語で来た人がCVした」という1対1の追跡は、BigQueryに入れてもできません。
そのため実務では、両方に共通する粒度で集計してから並べる形になります。代表的なのは「日付 × ランディングページ(URL)」です。Search Console側で「そのURLの表示回数・クリック数・平均順位」を、GA4側で「そのURLに着地したセッション数・CTAクリック数・コンバージョン数」をそれぞれ日別に集計し、URLと日付で突き合わせます。これで、「検索での露出は増えているのに、流入後の成果が伸びていないページ」のような見方ができます。
突き合わせるときは、日付の基準がずれる点に注意してください。Search Consoleの data_date は太平洋時間(Pacific Time)基準で、GA4のエクスポートはプロパティのタイムゾーン(日本のサイトなら通常は日本時間)基準です。日付単位で並べても、境界の数時間分がずれます。加えて、Search Consoleのデータは確定まで数日の遅れがあるため、直近数日は両者が揃っていないことも普通です。
GA4側のBigQuery連携とSQLについては、GA4とBigQueryの連携方法とGA4×BigQueryの使い方(SQL実践編)で解説しています。Search Console側のSQLに慣れたら、次はこの2本でGA4側を準備してください。
Search Console×BigQuery分析で注意したいこと
本記事で触れた注意点を、一覧にまとめます。SQLを書く前・結果を読む前のチェックリストとして使ってください。
| 項目 | 注意点 |
|---|---|
| 集計 | 1行=1クエリではない。必ずSUMなどで集計してからGROUP BYする(Google公式の推奨) |
| 期間 | data_date で必ず絞る。パーティションが効いて処理量と料金が減る |
| search_type | ウェブ・画像・動画・Discoverが混在している。目的に応じて 'WEB' などで絞る |
| 匿名化クエリ | 検索語は取得できない。クエリ別分析では is_anonymized_query で除外し、全体合計では含める |
| 平均掲載順位 | site_impression は sum_top_position、url_impression は sum_position。どちらも0ベースなので +1 する |
| data_dateのタイムゾーン | 太平洋時間基準。日本時間のGA4などと日付単位で比べるとずれる |
| データの遅延 | Search Consoleのデータは通常2〜3日で利用可能になる。直近数日は揃っていない前提で見る |
| 短期データ | 5日分のような短い期間の結果だけでSEO施策を決めない。28日・3か月に広げて確認する |
| AI生成SQL | そのまま信用しない。エラー文とスキーマを渡して修正させ、必ずBigQueryで実行して結果を確認する |
| 処理量とコスト | オンデマンド料金は処理バイト数で決まり、毎月1TiBまで無料。実行前にエディタの「This query will process ○○」表示を確認する習慣をつける |
コストについて補足します。SEGOで今回実行したSQLの処理量は、いずれも25KB〜80KB程度でした。BigQueryのオンデマンド料金には「1クエリあたり最低10MB」という下限がありますが、それでも毎月1TiBの無料枠に対しては、10万回実行してようやく届く量です。中小規模サイトのSearch Consoleデータを期間を絞って集計する限り、料金を心配する場面はほとんどありません。ただし、GA4のエクスポートなど他のデータと合算で無料枠を使う点は覚えておいてください。
Search Consoleデータを「見る」から「改善候補を抽出する」へ
BigQueryの価値は、Search Consoleの管理画面をそのまま再現することではありません。「表示回数50以上」「CTR 1%以下」「順位5位〜15位」のように、自社の状況に合わせた条件で分析対象を抽出できることです。管理画面では1ページずつ眺めるしかなかった「表示はされているのにクリックされていないページ」が、SQL1本で条件に合う順に並びます。
そのために必要なのは、本記事の4本のSQLと、3つの基本ルール(集計する・期間を絞る・search_typeを指定する)、そして匿名化クエリと掲載順位の列の扱いです。SQLを一から書けなくても、この前提を知っていれば、ChatGPTやClaudeに具体的な指示を出して、たたき台を作ってもらえます。エラーが出ても、エラー文とスキーマを渡せば直せます。
ただし、AIはSQLの補助者であって、判断者ではありません。SQLが正しいかはBigQueryの実行結果で確かめ、抽出されたURLをリライトするかは、期間を広げ、検索クエリと検索結果画面を見て、人間が決めます。
ここから先の発展としては、次のような方向があります。
- 期間を広げる:28日・3か月と期間を広げ、曜日変動を吸収したうえで同じSQLを実行する
- GA4と突き合わせる:日付×URLで集計し、検索での露出と流入後の成果を並べて見る
- SQLを定期実行する:BigQueryのスケジュール機能で毎週同じ集計を回し、改善候補の変化を追う
- Looker Studioなどで可視化する:SQLの結果をダッシュボードにして、チームで共有する
また、AI OverviewsやAI Modeでの表示状況は、BigQueryのテーブルには区別する列がなく、Search Consoleの管理画面で確認します。見方は第3回のSearch Consoleの生成AIレポートの見方で解説しています。
まずは本記事のSQLの日付を変えて、自分のサイトのデータで実行するところから始めてください。抽出した改善候補ページのタイトル・構造化データ・内部リンクを確認したいときは、SEGOの無料診断もあわせてご利用ください。
この記事を書いた人

岡 拓馬(おか たくま)
外資系SEOスペシャリスト / SEGO開発者
約10年の国際SEOコンサルティング経験
航空自衛隊で航空機整備員として勤務した後、2015年にフリーランスのWebライター・SEOコンサルタントとして独立。以来、アジア各国を拠点に海外ノマドワーカーとして活動。フィリピンの外資系企業でSEOスペシャリストとして従事した後、約10年の国際SEOコンサルティング経験をもとにSEO×AI検索の診断ツール「SEGO」を開発。著書に『AI時代のテクニカルSEOの教科書』(Kindle)、Udemy講座『AI時代のコンテンツSEOの教科書』がある。
執筆プロセス:本記事はAI(Claude Sonnet)による下書きを、岡拓馬が一次データ追加・実例追記・文意確認を行ったうえで公開しています。内容の最終責任は筆者(岡拓馬)が負います。