AI に書かせた GA4 の BigQuery SQL を検算する:セッション数・ユーザー数・購入率の 3 問を公開サンプルで比べた

GA4 の公開サンプル 1 日分で、質問文だけを渡して AI に書かせた 1 例の SQL と公式の定義どおりの SQL を実行して比べました。セッション数は 37 少なく、購入率は高めに出ました。原因と、自分のデータで検算する手順を紹介します。

19分で読めます Data
灰色の線で描いた SQL の画面と青い線で描いた SQL の画面が並び、それぞれから伸びた棒の高さが少し違うことを括弧で示した、AI の SQL を検算する様子の抽象イラスト
この記事は AI が下書きを補助し、運営者が内容を確認して公開しています。

GA4 のデータを BigQuery に出していて、集計の SQL(データベースからデータを取り出すための命令文)を ChatGPT や Claude に書かせ始めた方向けの記事です。AI に書かせた SQL の数字がどれだけずれるかを公開サンプルで実際に確かめ、自分のデータで検算する手順まで紹介します。

この記事で分かること

  • 質問文だけから書いた GA4 の SQL は、エラーなく動いても数字が公式の定義とずれることがある
  • 3 つの質問(セッション数・ユーザー数・購入したセッションの割合)のうち、どれがずれて、なぜずれたか
  • 自分のデータで AI の SQL を検算するときの手順とチェックリスト

ざっくり言うと:SQL は動くが、数字が定義とずれることがある

AI に SQL を書かせると、たいていはエラーなく実行でき、それらしい数字が返ってきます。ただし「エラーが出ない」ことと「数字が定義どおり」であることは別の話です。GA4 のエクスポートには、セッションの数え方などに独特の決まりがあるためです。

筆者が公開サンプルで試したところ、3 問のうち 2 問で数字がずれました。ずれ幅は 1.3% と小さめですが、月次の報告や前月比の判断に使うなら見過ごせない大きさです。

質問AI の SQL検算用 SQL判定
1 日のセッション数2,8122,849ずれた(37 少ない)
1 日のユーザー数2,5462,546一致した
購入したセッションの割合0.640%0.632%ずれた(高めに出た)

この表の数字は、筆者が 2026-11-05 に bq query(BigQuery をコマンドで操作する道具)で実行した結果です。以下、どう調べたかを順に説明します。

使うデータと 3 つの質問

データ:GA4 の公開サンプル(1 日分)

使ったのは、Google が誰でも使えるように公開している ga4_obfuscated_sample_ecommerce(GA4 の e コマースのサンプルデータ)です。Google Merchandise Store(Google のグッズを売る通販サイト)の、2020 年 11 月 1 日から 2021 年 1 月 31 日までの 3 か月分のイベントが入っています(Google for Developers)。

今回はそのうち最終日のテーブル events_20210131 だけを使います。行数は 26,489 で、筆者が bq show(テーブルの情報を見るコマンド)で確認しました。公開データなので、読者の方も同じ SQL で同じ数字を出せます。

料金:このサイズなら無料枠に収まる

BigQuery のオンデマンド料金(使った分だけ払う料金)は、クエリ(BigQuery に送る SQL の命令)が処理したデータ量で決まります。処理量は毎月 1 TiB(テビバイト。約 1 兆 1 千億バイト)まで無料です(Google Cloud の料金ページ)。

今回の 2 本の SQL は、dry run(実行せずに処理量だけを見積もる機能)でどちらも 5,798,375 バイト(約 5.8MB)でした。無料枠のおよそ 0.0005% です。

3 つの質問

AI に渡したのは、次の 3 つの質問の中身だけです。テーブルの構造(スキーマ)や GA4 の公式の定義は渡していません。

  1. 2021 年 1 月 31 日のセッション数はいくつか
  2. 同じ日のユーザー数はいくつか
  3. 同じ日に購入があったセッションの割合はいくつか

先にお断りしておくと、今回の「AI の答え」は ChatGPT などの画面で得た回答ではありません。この記事の作成で調査を担当した AI に、質問文だけを手がかりに書かせた SQL を 1 本だけ使っています。

プロンプト(AI に渡す指示文)の原文は保存していないため、ここでは質問の中身だけを示します。特定の AI サービスの実力を測る記事ではなく、「質問文だけで書いた SQL にどんな落とし穴があるか」を示す例として読んでください。

AI に書かせた SQL と結果

AI が書いた SQL は次のとおりです(筆者の手元の analysis/ai.sql)。

WITH e AS (
  SELECT user_pseudo_id, event_name,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key='ga_session_id') AS sid
  FROM `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_20210131`)
SELECT
  COUNT(DISTINCT sid) AS sessions,
  COUNT(DISTINCT user_pseudo_id) AS users,
  SAFE_DIVIDE(COUNT(DISTINCT IF(event_name='purchase', sid, NULL)), COUNT(DISTINCT sid)) AS purchase_session_rate,
  COUNTIF(event_name='purchase') AS purchase_events
FROM e

ここで出てくる用語を先に説明します。

用語意味
event_paramsイベントに付く追加情報の入れ物。キー(項目名)ごとに 1 行ずつ繰り返される入れ子の形になっている
UNNEST入れ子になった値を、表の行として取り出す BigQuery の書き方
ga_session_idセッション ID。セッションが始まった時刻を秒単位で表した数字
user_pseudo_idユーザーがサイトに初めてアクセスしたときなどに割り当てられる仮名の ID

event_params が繰り返しの形であることは、GA4 の公式ヘルプのスキーマ(テーブルの構造の説明)に書かれています(BigQuery Export のスキーマ)。ga_session_id が秒単位のタイムスタンプであることも公式の説明です(GA4 のセッションについて)。

この SQL を実行した結果は、セッション数 2,812、ユーザー数 2,546、購入したセッションの割合 0.640% でした。エラーは出ず、見た目にはもっともらしい数字です。

定義どおりの検算用 SQL と結果

次に、公式の定義に沿って書いた検算用の SQL です(筆者の手元の analysis/ref.sql から、記事で使う列だけを残しました)。

WITH e AS (
  SELECT user_pseudo_id, event_name,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key='ga_session_id') AS sid,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key='engagement_time_msec') AS eng_ms
  FROM `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_20210131`),
s AS (
  SELECT CONCAT(user_pseudo_id, '-', CAST(sid AS STRING)) AS skey, event_name
  FROM e WHERE sid IS NOT NULL)
SELECT
  (SELECT COUNT(DISTINCT skey) FROM s) AS sessions,
  (SELECT COUNT(DISTINCT user_pseudo_id) FROM e) AS total_users,
  (SELECT COUNT(DISTINCT user_pseudo_id) FROM e
     WHERE eng_ms > 0 OR event_name = 'first_visit') AS active_users,
  (SELECT COUNT(DISTINCT IF(event_name='purchase', skey, NULL)) FROM s) AS purchase_sessions,
  (SELECT COUNT(DISTINCT IF(event_name='purchase', skey, NULL)) FROM s)
    / (SELECT COUNT(DISTINCT skey) FROM s) AS purchase_session_rate,
  (SELECT COUNTIF(event_name='session_start') FROM e) AS session_start_events

違いは、セッションを user_pseudo_id と ga_session_id の組 で数えている点です。公式ヘルプは、セッションを 1 つずつ見分けるには、ga_session_id を user_id(サイト側でログインした人に付ける ID)または user_pseudo_id と結び付けるよう勧めています(GA4 のセッションについて)。

ユーザー数は 2 種類を出しました。GA4 には「合計ユーザー数」と「アクティブ ユーザー数」という別々の指標があるためです(ユーザー指標の説明)。アクティブ ユーザーの条件は、公式の定義を SQL で近似したもので、GA4 の画面の数字とは照らし合わせていません。

2 本の SQL の検算結果

セッション数は AI の SQL が 2,812、検算用 SQL が 2,849 で AI の SQL が 37 少なく、ユーザー数はどちらも 2,546 で一致したことを示す縦棒グラフ

図 1 のとおり、セッション数だけが目に見えてずれ、ユーザー数は同じ値になりました。購入したセッションの割合は値が小さいので、1 万セッションあたりの件数に直して図 2 にしています。

購入したセッションの割合を 1 万セッションあたりで比べると、AI の SQL が 64、検算用 SQL が 63.2、購入イベント数をセッション数で割った場合が 66.7 で、検算用が最も低いことを示す縦棒グラフ

指標AI の SQL検算用 SQL差
セッション数2,8122,84937(検算用の 1.3%)
合計ユーザー数2,5462,5460
アクティブ ユーザー数(近似)出していない2,459合計ユーザーより 87 少ない
購入があったセッション18180
購入したセッションの割合0.640%0.632%0.008 ポイント
同(1 万セッションあたり)6463.20.8

差と割合は、筆者が実行結果から計算しました。AI の SQL の購入セッション数は、0.640% に 2,812 を掛けて逆算した値(18)です。

ずれの原因(3 問それぞれ)

3 つの質問それぞれについて、AI の SQL で起きたことと直し方を並べた比較表。セッション数は ID の組で数え直す、ユーザー数は SQL は一致だが比べる指標を先に決める、購入セッション率は分母を直す、と整理している

図 3 は、3 問それぞれで何が起きて、どう直すかをまとめたものです。順に詳しく見ていきます。

問 1:セッション数は「ID だけ」で数えたので少なく出た

AI の SQL は ga_session_id だけを数えていました。ところが ga_session_id は「セッションが始まった秒」なので、同じ秒に始まった別の人のセッションは同じ番号になります。筆者が調べると、同じ ga_session_id を最大 3 人の user_pseudo_id が持っていました。

別の人のセッション 2 つ
  user_pseudo_id = A, ga_session_id = 1612051200
  user_pseudo_id = B, ga_session_id = 1612051200
        ↓ ga_session_id だけで数える
  1 セッション(2 つが 1 つにまとまる)
        ↓ user_pseudo_id と組にして数える
  2 セッション(正しく分かれる)

上の ID の値は説明用の例です。この「まとまってしまう」分が合計 37 セッションあり、AI の SQL は 2,812、検算用は 2,849 になりました。

問 2:ユーザー数は一致したが、比べる指標で差が出る

ユーザー数は、AI の SQL も検算用も user_pseudo_id の重複を除いて数えたので、同じ 2,546 でした。これは GA4 の「合計ユーザー数」(期間中にサイトに来た人の合計)に当たります。

一方、GA4 には「アクティブ ユーザー数」(実際にサイトを利用した人の数)という別の指標があります(ユーザー指標の説明)。近似の条件で数えると 2,459 で、合計ユーザーより 87 少なくなりました。AI の SQL が正しくても、報告先がアクティブ ユーザーを見ているなら 3.4% の食い違いになります。

問 3:購入率は分母のずれがそのまま伝わった

公式ヘルプでは、セッションのキーイベント率(購入などの成果が出たセッションの割合)を「キーイベントが発生したセッションの数 ÷ セッションの総数」と定義しています(キーイベント率の説明)。購入したセッションの割合も同じ考え方で計算できます。

分子の「購入があったセッション」は、AI の SQL も検算用も 18 でした。違ったのは分母で、AI の SQL は問 1 と同じく 2,812 と少なく数えています。

分母が小さいぶん割合が大きくなり、0.640% と高めに出ました。つまり、問 1 を直せば問 3 も直ります。

検算の手順(自分のデータで試すときのチェックリスト)

質問を 1 文で書く、AI に SQL を書かせて実行する、公式ヘルプで定義を確認する、定義どおりの検算用 SQL を実行する、数字が一致すれば報告に使い、ずれたら原因を確かめて直した SQL でもう一度比べる、という検算の流れを示したフロー図

図 4 の流れを、作業の手順に直すと次のようになります。

  1. 知りたいことを 1 文の質問にする
  2. AI に SQL を書かせて実行し、出た数字を控える
  3. 使っている指標の定義を、GA4 の公式ヘルプで確認する
  4. 定義どおりの検算用 SQL を自分で書き、同じテーブル・同じ日付で実行する
  5. 2 つの数字を並べて比べる
  6. ずれたら、下のチェックリストで原因を探し、直した SQL でもう一度比べる
確認すること見る場所今回の結果
セッションを user_pseudo_id と ga_session_id の組で数えているかCOUNT(DISTINCT …) の中身AI の SQL は ID だけだった
割合の分母は「セッション数」で、分子は「成果のあったセッション数」か割り算の上と下分子は正しく、分母が少なかった
「ユーザー数」が合計ユーザーかアクティブ ユーザーか比べる相手の指標名合計ユーザーで一致した
event_params を取り出すときに行が増えていないかUNNEST の書き方今回は増えていない
テーブルの日付と範囲は合っているかFROM のテーブル名2 本とも同じ 1 日分

つまずきやすい点

session_start の件数をセッション数にしてしまう

セッションが始まると session_start イベントが記録されます(GA4 のセッションについて)。そのため「session_start を数えればセッション数になる」と考えがちです。

ところが今回のテーブルでは、session_start は 2,760 件で、検算用のセッション数 2,849 より 89 少なくなりました。差が出たのは、前日から続いているセッションの session_start が、その日のテーブルには記録されていないためと考えられます。

購入イベントの件数で割ってしまう

purchase イベントは 19 件ありましたが、購入があったセッションは 18 でした。1 つのセッションで購入が 2 回以上記録された例があったと考えられます。イベント数をセッション数で割ると 0.667% となり、定義どおりの 0.632% より大きく出ます(図 2 の右端の棒)。

今回の AI の SQL はこの誤りを犯していませんでしたが、よく見かける書き方なので注意してください。

UNNEST を JOIN で書いて行が増える

event_params はキーごとに繰り返される形です(BigQuery Export のスキーマ)。テーブルと UNNEST(event_params) を JOIN(2 つの表を行どうしで結び付ける SQL の命令)でつなぐ書き方にすると、1 つのイベントがキーの数だけ行に増えます。その状態で COUNT(*) を使うと件数が膨らみます。

今回の 2 本の SQL は、必要なキーだけを 1 つ取り出す書き方なので、この問題は起きていません。

GA4 の画面の数字と完全には一致しない

GA4 の画面のセッション数は、HyperLogLog++ という計算方法で推定した値です。一方、BigQuery では正確な数を計算するため、この推定は使われません(GA4 のセッションについて)。画面と BigQuery の数字が少し違っても、それだけで SQL の誤りとは言えません。

また、今回の公開サンプルは難読化(元の値を分からないように加工すること)されています。公式の説明では、データの内部の整合性が少し損なわれている可能性があり、デモアカウントの画面とは比べられないとされています(Google for Developers)。

当日や直近のテーブルの数字は後から変わる

日次テーブル events_YYYYMMDD には、その日のイベントが最長 3 日後まで追加で記録されます(BigQuery Export のスキーマ)。直近の日付で検算すると、AI の SQL と検算用 SQL を実行した時刻の違いで数字がずれることがあります。2 本は続けて実行するか、3 日以上前の日付で比べると安心です。

まとめ

  • 質問文だけから書いた GA4 の SQL は、エラーなく動いても数字がずれることがあります。今回はセッション数が 37(1.3%)少なく、購入率が高めに出ました
  • ずれの元は、ga_session_id を user_pseudo_id と組にせずに数えたことです。分母のずれが購入率にもそのまま伝わりました
  • AI の SQL は、公式の定義どおりの検算用 SQL と同じテーブルで並べて実行してから報告に使いましょう

次にやること

集計した数字をどう見せるかの参考には、ダッシュボードの一覧で、このサイトが公開データから作ったダッシュボードの例をご覧ください。AI に書かせた SQL が定義どおりか、第三者の目で確かめたい場合は、BigQuery SQL レビューパックもご利用いただけます。

参考にした一次情報

更新履歴

  • 2026-11-09: 定期点検で、本文の11か所を直しました。