学べること
- 4種類のパーティション戦略の実装方法と検証結果
- TIMESTAMP パーティションのエラーとその回避方法
- パーティションフィルタ強制によるコスト削減テクニック
- パーティション有効期限による自動データ削除
はじめに
BigQueryのパーティショニングは、大規模テーブルのクエリコストとパフォーマンスを劇的に改善する重要な機能です。本記事では、dbt + BigQueryで利用可能な4種類のパーティション戦略を実際に検証し、成功パターンと失敗パターンを明らかにします。
検証環境:
- dbt 1.11.5 + dbt-bigquery 1.11.0
- BigQueryプロジェクト: sdp-sb-yada-29d2
- データセット: dbt_sandbox
- 検証日: 2026-02-17
検証結果: 6項目中4成功、2失敗(学習目的)
パーティション戦略の選択フローチャート
flowchart TD Start[テーブル要件] --> Q1{時系列データ?} Q1 -->|Yes| Q2{日付列の型は?} Q1 -->|No| Q3{範囲で分割可能?} Q2 -->|DATE型| DatePart[DATE パーティション<br/>partition_by: field, data_type: date] Q2 -->|TIMESTAMP型| TimestampPart[DATE変換が必要<br/>DATE関数でラップ] Q2 -->|日付列なし| IngestionPart[Time-ingestion<br/>partition_by: data_type: time] Q3 -->|Yes 整数範囲| IntPart[INT64 RANGE パーティション<br/>partition_by: data_type: int64] Q3 -->|No| NoPart[パーティションなし] DatePart --> Q4{コスト削減重視?} TimestampPart --> Q4 IntPart --> Q4 Q4 -->|Yes| Filter[require_partition_filter: true] Q4 -->|データ保持期間あり| Expiration[partition_expiration_days: N]
DATE と INT64 RANGE パーティション - 推奨パターン
DATE パーティション ✅
設定例:
config:
materialized: table
partition_by:
field: order_date
data_type: date
granularity: day検証結果:
- 実行時間: 5.10秒
- パーティション: 日付ごとに作成
- BigQueryコンソールでパーティション情報確認可能
ユースケース:
- ✅ 日次バッチ処理
- ✅ 時系列分析
- ✅ ログデータ、イベントデータ
コスト削減効果:
-- ✅ パーティションフィルタあり(推奨)
SELECT * FROM partition_date_demo
WHERE order_date = '2024-01-01'
-- → 1日分のみスキャン
-- ❌ パーティションフィルタなし(非推奨)
SELECT * FROM partition_date_demo
WHERE customer_id = 1
-- → 全パーティションスキャン(コスト高)INT64 RANGE パーティション ✅
設定例:
config:
materialized: table
partition_by:
field: customer_id
data_type: int64
range:
start: 0
end: 1000
interval: 10BigQueryでの実装:
CREATE TABLE `dbt_sandbox.partition_int_demo`
PARTITION BY RANGE_BUCKET(customer_id, GENERATE_ARRAY(0, 1000, 10))
AS (...)検証結果:
- 実行時間: 4.16秒
- パーティション: customer_id を 10刻みで分割(0-9, 10-19, …)
ユースケース:
- ✅ ユーザーID範囲による分割
- ✅ 地域コード、店舗IDなどの範囲分割
- ✅ 時系列以外のパーティショニング
注意点:
- 範囲外の値(<0 または >=1000)は特別なパーティションに格納される
intervalは均等分割のみ(カスタム境界値はGENERATE_ARRAYで指定)
TIMESTAMP と Time-ingestion - 制約と回避方法
TIMESTAMP パーティション ❌
設定例(失敗):
config:
materialized: table
partition_by:
field: order_date
data_type: timestamp
granularity: day検証結果: ❌ 失敗
Database Error:
PARTITION BY expression must be DATE(<timestamp_column>),
TIMESTAMP_TRUNC(<timestamp_column>, DAY/HOUR/MONTH/YEAR), ...
原因: BigQueryはTIMESTAMP列を直接パーティションキーにできない
正しい設定:
方法1: DATE関数でラップ
config:
materialized: table
partition_by:
field: "DATE(order_date)"
data_type: date
granularity: day方法2: モデル内でDATE列を作成
SELECT
order_id,
customer_id,
order_date,
DATE(order_date) AS order_date_only -- この列でパーティション
FROM {{ ref('stg_orders') }}重要な学び:
- ⚠️ TIMESTAMP列は
DATE()またはTIMESTAMP_TRUNC()で変換が必要 - ⚠️ dbt-bigqueryの
data_type: timestampは 直接は使えない
Time-ingestion パーティション ❌
設定例(失敗):
config:
materialized: table
partition_by:
data_type: time
granularity: day検証結果: ❌ 失敗
Runtime Error: Could not parse partition config
原因: dbt-bigquery 1.11.0でのTime-ingestionパーティション設定構文が不明確
BigQueryでの正しいSQL:
CREATE TABLE `dbt_sandbox.partition_ingestion_demo`
PARTITION BY _PARTITIONTIME -- 疑似列を使用
AS (...)dbtでの代替方法:
方法1: post-hookで設定
{{
config(
materialized='table',
post_hook=[
"ALTER TABLE {{ this }} SET OPTIONS (partition_expiration_days=90)"
]
)
}}方法2: DATE列を追加してパーティション
SELECT
*,
CURRENT_DATE() AS ingestion_date
FROM {{ ref('source') }}重要な学び:
- ⚠️ Time-ingestion パーティションはdbt-bigqueryで直接サポートが不完全
- ✅ 代替:
CURRENT_DATE()やCURRENT_TIMESTAMP()を列として追加してパーティション
パーティションフィルタ強制とデータ有効期限
require_partition_filter ✅
設定例:
config:
materialized: table
partition_by:
field: order_date
data_type: date
require_partition_filter: true検証結果: ✅ 成功
- テーブル作成成功
- パーティションフィルタなしのクエリは エラーになる
クエリ例:
-- ✅ 成功(パーティションフィルタあり)
SELECT * FROM partition_filter_required_demo
WHERE order_date = '2024-01-01';
-- ❌ エラー(パーティションフィルタなし)
SELECT * FROM partition_filter_required_demo
WHERE customer_id = 1;
-- Error: Cannot query over table without a filter over column(s) 'order_date'ユースケース:
- ✅ 大規模テーブルで意図しない全件スキャンを防止
- ✅ コスト管理が重要なプロジェクト
- ✅ アナリストが直接クエリする環境
推奨設定:
- 本番環境の大規模factテーブルには 必ず設定すべき
- 開発環境では柔軟性のため
falseも検討
partition_expiration_days ✅
設定例:
config:
materialized: table
partition_by:
field: order_date
data_type: date
partition_expiration_days: 7検証結果: ✅ 成功
- 実行時間: 5.22秒
- 7日以上前のパーティションは自動削除される
ユースケース:
- ✅ ログデータ(保持期間: 30日、90日など)
- ✅ 一時的な分析テーブル
- ✅ GDPRなどの法的要件(データ削除)
コスト削減効果:
データ保持期間 7日:
- ストレージコスト: 1/52 (365日 → 7日)
- 自動削除: 管理コストなし
注意点:
- ⚠️ 削除は 取り消せない(バックアップ推奨)
- ⚠️ テーブルレベルの
hours_to_expirationとは併用できない
おわりに
パーティション設定まとめ
| パーティション方式 | 状態 | 推奨度 | ユースケース |
|---|---|---|---|
| DATE | ✅ | ⭐⭐⭐⭐⭐ | 日次データ、時系列分析 |
| TIMESTAMP | ⚠️ | ⭐⭐ | DATE変換が必要(推奨しない) |
| INT64 RANGE | ✅ | ⭐⭐⭐⭐ | ユーザーID範囲、地域コード |
| Time-ingestion | ❌ | ⭐ | dbtサポート不完全(代替手段推奨) |
| require_partition_filter | ✅ | ⭐⭐⭐⭐⭐ | 大規模テーブルに必須 |
| partition_expiration_days | ✅ | ⭐⭐⭐⭐ | ログ、一時データ |
ベストプラクティス
大規模テーブルの推奨設定:
config:
materialized: table
partition_by:
field: event_date
data_type: date
require_partition_filter: true
partition_expiration_days: 90参考
- BigQuery Partitioned Tables
- dbt-bigquery Partition Configuration
- Models Index - カテゴリ全体の概要
- パーティショニング&クラスタリングガイド - 専門ガイド
最終更新: 2026-02-17