▼概要
BigQueryやSnowflakeで月単位のデータを集計する際、
WHERE句で指定月をどのように表現するかは多くのバリエーションがある。結果は同じでもクエリ実行時間・コストの面での違いが出ることに注意すべきだと考える。
本記事では、以下2つの指定方法についてBigQueryにおけるクエリ実行時間(スロット消費時間)の観点で比較・検証する。その中で、Snowflakeとも適宜比較する。
-
BETWEENを用いて指定月を直接指定する方法 -
DATE_TRUNCを用いて日付を月単位に丸めて比較する方法- (where句内で関数などにて処理を行う方法)
▼前提条件
- BigQueryのスロット消費時間による課金プランを前提とする
(オンデマンド課金とは異なる点に注意) - GA4 の BigQuery エクスポートテーブル(events_*)を使用
- event_date カラムは STRING 型(YYYYMMDD)
▼背景と問題意識
月単位でデータを絞り込む場合、以下のようなクエリを見かけることがある。
WHERE DATE_TRUNC(PARSE_DATE('%Y%m%d', event_date), MONTH) = DATE '2025-12-01'
一方で、GA4 の event_date は YYYYMMDD 形式の STRING であるため、
以下のような BETWEEN による指定も可能である。
WHERE event_date BETWEEN '20251201' AND '20251231'
この 2 つの書き方は 結果として取得される行は同じだが、クエリの実行時間(スロット消費時間)に差が出るのではないか、という疑問を持った。
今回はBigQueryにて検証を行うが、where句での月指定方法でクエリ実行時間が変わるという点はSnowflakeなどにおいても同じである。私は業務でBigQuery, Snowflake両方を触るため両方を比較しつつ進める。
▼前提知識と仮説
前提知識
BigQuery におけるクエリ実行は、概ね以下の流れで行われる。
- クエリを解析し、実行計画を作成
- ストレージから必要な列データを読み込む(物理スキャン)
- 読み込まれた行データに対してWHERE句の条件を 各行ごとに評価
- 条件を満たした行を後続の演算(集計など)に渡す
仮説
-
DATE_TRUNCをWHERE句で使用すると、各行に対して関数評価が発生する - 読み込む行数自体もwhere句の記述により影響を受ける
- Snowflakeと同様
- その結果、WHERE句の評価コストが増大し、スロット消費時間が増える
- BETWEEN を用いた単純比較の方が、行ごとの処理が軽くなり実行時間が短くなる
▼検証手順
GA4のevents_*テーブルを対象に、
2025年12月のデータ件数をCOUNT(*)で集計する。
① BETWEEN を用いた月指定
SELECT
COUNT(*)
FROM
`hogehoge.analytics_hogehoge.events_*`
WHERE
event_date BETWEEN '20251201' AND '20251231';
② DATE_TRUNC を用いた月指定
SELECT
COUNT(*)
FROM
`hogehoge.analytics_hogehoge.events_*`
WHERE
DATE_TRUNC(
PARSE_DATE('%Y%m%d', event_date),
MONTH
) = DATE '2025-12-01';
結果
クエリ事項詳細の結果は以下の通り。
| 月指定方法 | 読み取り行数 | スロット消費時間 |
|---|---|---|
| BETWEEN | 4,672 | 約 532 ms |
| DATE_TRUNC | 9,334 | 約 984 ms |
補足:
- WHERE句なしの場合の行数は 9,334
- ドライラン結果より 物理スキャン量(bytes processed)は両者で同一であると分かっている
⇒BETWEENでの月指定の方が約2倍ほど読み取り行数とスロット消費時間が短く、コストも半分ほどとなった
しかし、読み取り行数は実際には変わらないが裏側で_table_suffixにて読み取る行数が削減されていた
betweenを使ったクエリの実行計画を見ると以下の通り、
$1:_TABLE_SUFFIX
FROM hogehoge.analytics_hogehoge.events_*
WHERE between($1, '20251201', '20251231')
$20 := COUNT_STAR()
$20
TO __stage00_output
⇒最初に_table_suffixを活用していることが分かる。_table_suffixという日ごとのテーブルの読み込みをevent_dateから自動で行われていることが推察出来る。
⇒betweenという記法(関数評価を行わない方法)により直接的に読み込み行数が減っていたわけではなかった可能性がある
※event_dateではなく、event_nameにて再度検証を行ってみたところ以下。
| event_name指定方法 | 読み取り行数 | スロット消費時間 |
|---|---|---|
= |
9334 | 約 542 ms |
regexp_contains |
9,334 | 約 998 ms |
⇒読み取り行数は変わらず、スロット消費時間にのみ大きく違いが出た。
⇒**DATE_TRUNCにより、読み取り行数が増えていたわけではなかった**
★まとめと考察
上記の検証から、BigQueryにおいて月単位でデータを絞り込む場合、
WHERE句でDATE_TRUNCなどの関数処理を用いた記述は、可能な限り避けるべきであることが分かった。
これらの記述では各行に対して関数評価が発生するため、WHERE句の評価コストが増大し、結果としてスロット消費時間やクエリ実行時間が長くなる。
一方で、当初の仮説として挙げていた**「WHERE句の書き方によって、読み込む行数そのものが変化する」という点については、BigQuery においては誤りである**ことも分かった。
BigQuery では、原則としてスキャンされる行数は FROM 句によって決まり、WHERE 句はスキャンされた行に対する条件評価を定義するものであり、読み込む行数自体を減らす役割は持たない(_TABLE_SUFFIX を利用した場合を除く)。
この挙動は Snowflake とは異なる可能性があり、Snowflake では WHERE 句の記述内容によって物理的な読み込み行数が変化するケースも考えられるため、この点については別途検証したい。
Snowflakeによっては本記事での比較によるクエリ実行時間の差がより大きくなるのでは無いかと予想する。
【上記やBigQueryへの理解を深めるためのリンク】
