前提: GA4とシャーディングテーブル
GA4のイベントデータを仕事で取り扱う人は多いと思う。BigQueryにexportする場合、GA4のイベントデータは[PREFIX]_YYYYMMDD
のシャーディングテーブルになっており、さらにその中でevents_YYYYMMDD
とevents_intraday_YYYYMMDD
という2種類のシャーディングが存在する。
公式のドキュメントでは「パーティション分割テーブルのほうがパフォーマンスが向上するため、テーブルのシャーディングよりもパーティショニングをおすすめします」と記載されている通り、でかいテーブルを新規に作る場合はパーティション分割テーブルを作るほうがよい。
しかし、GA4のBigQueryにexportする場合、GA4より前のユニバーサル アナリティクスの時代以前からシャーディングでテーブルが保存されていたという背景がある。そのため、GA4のイベントデータもシャーディングテーブルとしてデータが日々exportされる。
シャーディングテーブルを使っていて、クエリが失敗するパターン
events_YYYYMMDD
とevents_intraday_YYYYMMDD
の両方をワイルドカードで指定して、以下のようなクエリを書くことは多いだろう。
SELECT collected_traffic_source.manual_campaign_name, collected_traffic_source.manual_marketing_tactic, FROM `my-project.my_dataset.my_events_*`
あるいはワイルドカードを指定しつつ、intradayは除外するというユースケースも多い。
SELECT collected_traffic_source.manual_campaign_name, collected_traffic_source.manual_marketing_tactic, FROM `my-project.my_dataset.my_events_*` WHERE starts_with(_TABLE_SUFFIX, 'intraday_')
しかし、このクエリは失敗するケースがある。具体的にはevents_YYYYMMDD
とevents_intraday_YYYYMMDD
のスキーマが異なる場合だ。GA4のスキーマは経験的に半年から一年に一回くらいの頻度で新しいカラムが追加されることがある(上記の例だとmanual_marketing_tactic
が該当)。つい先日も追加されたばかりだ。
GA4のBigQueryエクスポートテーブルに新フィールド登場。
— Ich (@S4RngeNhBvAlC9f) 2024年7月12日
これ、STRUCT型の中の構造が変わるからスケジュールで更新するようなクエリ動かしてるとエラー吐くパターンあると思う。
manual_source_platform
manual_creative_format
manual_marketing_tactic pic.twitter.com/jsitWffLNM
また、先日新規に追加されたカラムはGA4の公式のドキュメントは2024/07/17現在まだ情報として反映されておらず、事前に検知するのは現実的に難しい*1。
GA4側の変更により、新規カラムが追加されスキーマが変更された場合、タイミングによってはevents_YYYYMMDD
とevents_intraday_YYYYMMDD
スキーマが異なり、クエリが落ちてしまう*2ことがありえる。
具体例を見てみよう。検証のための一時テーブルを作成する。events_YYYYMMDD
は以下のようになっており、既存のカラム(manual_campaign_name
)と新規のカラム(manual_marketing_tactic
)が両方入った状態のスキーマになっている。
CREATE OR REPLACE TABLE `my-project.my_dataset.my_events_20240716` AS ( SELECT STRUCT('hoge' AS manual_campaign_name, 'fuga' AS manual_marketing_tactic) AS collected_traffic_source )
一方、events_intraday_YYYYMMDD
は以下のようにmanual_marketing_tactic
カラムがまだ存在せず、スキーマがずれていたとする。
CREATE OR REPLACE TABLE `my-project.my_dataset.my_events_intraday_20240716` AS ( SELECT STRUCT('hoge' AS manual_campaign_name) AS collected_traffic_source )
この場合、以下のクエリは通るが
SELECT collected_traffic_source.manual_marketing_tactic, FROM `my-project.my_dataset.my_events_*`
片方にしか存在しないカラムがある場合、クエリが落ちる(Field name manual_marketing_tactic does not exist in STRUCT<manual_campaign_name STRING> at [3:28]
のようなエラーが返ってくる)。
SELECT collected_traffic_source.manual_campaign_name, collected_traffic_source.manual_marketing_tactic, FROM `my-project.my_dataset.my_events_*`
クエリが落ちるのを避けようとして、以下のクエリを書いたとしても同様のエラーで失敗する。これはクエリを実際に走らせる前に、BigQuery側でクエリ内にあるシャーディングテーブルのスキーマを先にチェックする形式になっているからではないか、と思われる。
SELECT collected_traffic_source.manual_campaign_name, collected_traffic_source.manual_marketing_tactic, FROM `my-project.my_dataset.my_events_*` WHERE starts_with(_TABLE_SUFFIX, 'intraday_')
そのため、これを回避したいのであれば、以下のようにFROM
句側でintradayが入ってこないようにするしかない(書き方がイマイチなのは認める)。
SELECT collected_traffic_source.manual_campaign_name, collected_traffic_source.manual_marketing_tactic, FROM `my-project.my_dataset.my_events_20*`
追記
my_events_20*
のような指定をする場合、WHERE句に入ってくる_TABLE_SUFFIX
は20240716
のような形式ではなく240716
のようになってしまうので、さらに書き方的に微妙ですね。。
まとめ
- GA4をBigQueryにexportする場合、GA4側の修正によりスキーマが変わっていることがある
- 外部SaaSから提供されるデータを扱う場合、
SELECT *
ではなく明示的にカラムを列挙する形を取り、クエリが失敗しないように固い作りにしておこう
- 外部SaaSから提供されるデータを扱う場合、
- 同じGA4のデータであっても、
events_YYYYMMDD
とevents_intraday_YYYYMMDD
でスキーマが異なる場合があり、その場合もクエリがこける- よりクエリを頑健にしたいのであれば、intradayの除外の仕方を工夫しよう