Cloud Ace Tech Blog
🗓️

Dataform の updatePartitionFilter で複数の動的日付フィルタを指定する

こんにちは、クラウドエース株式会社第四開発部の渡辺です。

Dataform で incremental テーブルを運用していると、「特定の日付範囲だけ再計算したい」という場面が出てきます。
本記事では、updatePartitionFilter開始日・終了日のような複数の動的条件 を載せる方法を紹介します。

概要

incremental テーブルの更新対象パーティションは、updatePartitionFilter で絞り込めます。
期間を実行時に変えたい場合は dataform.projectConfig.vars を使い、条件が複数ある場合は JavaScript の式で 1 本の SQL 条件文字列 を組み立てます。

updatePartitionFilter:
  (!!dataform.projectConfig.vars.start_date && !!dataform.projectConfig.vars.end_date)
    ? `date >= DATE('${dataform.projectConfig.vars.start_date}') AND T.date <= DATE('${dataform.projectConfig.vars.end_date}')`
    : `date >= DATE_SUB(CURRENT_DATE('Asia/Tokyo'), INTERVAL 7 DAY)`

はじめに

BigQuery 上のテーブルを Dataform で incremental に更新する場合、毎回すべてのパーティションを書き換えるのではなく、対象期間のパーティションだけ を更新したいことがあります。

その絞り込みに使うのが updatePartitionFilter です。
固定の日付文字列でも動きますが、次のような要件になると途端に扱いづらくなります。

  • 実行のたびに開始日・終了日を変えたい
  • 開始日と終了日の両方で範囲を指定したい

本記事では、静的な書き方から始め、vars による動的指定、そして複数条件を 1 つのフィルタに載せる書き方まで順に説明します。

incremental と updatePartitionFilter

Dataform の incremental テーブルでは、差分更新の対象をパーティション単位で制御できます。
updatePartitionFilter には、更新対象とするパーティションを表す SQL の条件式(文字列) を渡します。

イメージとしては、次のような条件で「どの日付のパーティションを更新するか」を指定します。

date >= DATE('2026-07-01')

この条件に合うパーティションだけが、今回の実行で更新対象になります。

静的な日付フィルタの例

まずは固定日付での指定です。

config {
    type: "incremental",
    uniqueKey: ["id"],
    bigquery: {
        partitionBy: "date",
        updatePartitionFilter: `date >= DATE('2026-07-01')`
    }
}

この書き方は単純で分かりやすい反面、期間を変えるたびに定義を修正する必要があります。
「昨日から今日まで」「先月の全期間」など、実行ごとに期間が変わる運用では向きません。

動的にしたい・条件を複数にしたい

実務では、次の 2 つが同時に求められることが多いです。

  1. 動的: 実行時に期間を渡したい
  2. 複数条件: 開始日以上かつ終了日以下、のように範囲で絞りたい

updatePartitionFilter に渡せるのは最終的には 1 つの条件文字列 です。
そのため、開始日と終了日を別々のオプションとして渡すのではなく、Dataform 側の JavaScript で文字列を組み立て、AND でつなぐ必要があります。

projectConfig.vars で日付を渡す

実行時の値は dataform.projectConfig.vars 経由で参照できます。
たとえば start_dateend_date を vars として渡すと、定義側では次のように読めます。

dataform.projectConfig.vars.start_date
dataform.projectConfig.vars.end_date

vars の渡し方の例です(CLI の場合)。

dataform run --vars=start_date=2026-07-01,end_date=2026-07-31

dataform.jsonvars にデフォルト値を置いておく運用もあります。
どちらにしても、テーブル定義からは dataform.projectConfig.vars.* として参照する点が共通です。

複数条件を updatePartitionFilter に載せる

ここが本題です。
vars が両方そろっているときだけ範囲指定の文字列を作り、そうでないときはフォールバックの条件を使う、という形にすると扱いやすいです。

このコードには 2 種類の日付フィルタが登場します。

  • updatePartitionFilter(config 内):MERGE 文の ON 句に展開され、今回の実行で書き換えるパーティションの範囲を指定します。
  • dateFilter(js ブロック内):SELECT の WHERE 句に展開され、ソーステーブルから読み込む行の範囲を指定します。

2 つを同じ期間で揃えることで、書き換え対象のパーティションだけを絞り込みつつ、無駄なデータスキャンも防げます。

SQLX

config {
    type: "incremental",
    schema: dataform.projectConfig.defaultDataset,
    name: "target_table",
    tags: ["target_table"],
    uniqueKey: ["id"],
    bigquery: {
        partitionBy: "date",
        updatePartitionFilter: 
            (!!dataform.projectConfig.vars.start_date && !!dataform.projectConfig.vars.end_date)
                ? `date >= DATE('${dataform.projectConfig.vars.start_date}') AND T.date <= DATE('${dataform.projectConfig.vars.end_date}')`
                : `date >= DATE_SUB(CURRENT_DATE('Asia/Tokyo'), INTERVAL 7 DAY)`
    }
}

/* 実行時引数(vars)が存在する場合はその範囲、未指定時は「直近 7 日間」をフィルタ条件として生成 */
js {
    const start_val = dataform.projectConfig.vars.start_date ? `'${dataform.projectConfig.vars.start_date}'` : "NULL";
    const end_val = dataform.projectConfig.vars.end_date ? `'${dataform.projectConfig.vars.end_date}'` : "NULL";
    var dateFilter = `date >= COALESCE(DATE(${start_val}), DATE_SUB(CURRENT_DATE('Asia/Tokyo'), INTERVAL 7 DAY))
        AND date <= COALESCE(DATE(${end_val}), CURRENT_DATE('Asia/Tokyo'))`;
}

SELECT
    id,
    date,
    value
FROM ${ref("source_table")}
WHERE ${dateFilter}

補足: updatePartitionFilterdateFilter では、変数が片方だけ渡された場合の挙動が異なります。

  • updatePartitionFilter は両変数が揃っていないとフォールバック条件に切り替わります
  • dateFilter は各変数を独立して COALESCE するため、start_date だけ渡すと終了日には当日が使われます

片方のみの指定を運用上禁止する場合は、dateFilter 側も三項演算子に統一すると一貫性が高くなります。

workflow_settings.yaml

# Dataform コアのバージョン設定
dataformCoreVersion: 3.0.0

# BigQuery のデフォルト接続設定
defaultProject: "your-gcp-project"
defaultDataset: "test_dataset"
defaultLocation: "asia-northeast1"
defaultAssertionDataset: "dataform_assertions"

# vars のデフォルト値(CLI や API で上書きされなかった場合に使われます)
vars:
  start_date: "2026-07-01"
  end_date: "2026-07-15"

ポイントを分解すると次のとおりです。

1. vars の存在チェック

!!dataform.projectConfig.vars.start_date && !!dataform.projectConfig.vars.end_date

start_dateend_date の両方が渡されているかを確認しています。
片方だけ渡された状態で不完全な SQL を生成しないためのガードです。

2. 三項演算子で条件文字列を切り替える

条件 ? `範囲指定の SQL` : `フォールバックの SQL`

updatePartitionFilter には常に有効な条件文字列を渡したいので、未設定時の挙動も明示します。
上の例では「直近 7 日」をデフォルトにしていますが、運用に合わせて変えてください。

3. テンプレートリテラルで SQL を組み立てる

`date >= DATE('${dataform.projectConfig.vars.start_date}') AND T.date <= DATE('${dataform.projectConfig.vars.end_date}')`

vars の値を SQL リテラルに埋め込みます。
複数条件は、ここで SQL の AND としてつなぎます。
2 つ目以降の条件では、パーティション カラムに T. を明示する点が重要です(理由は後述の注意点を参照)。

実装時の注意点

1. 未設定時の挙動を決めておく

三項演算子の false 側を省略したり、空文字を返したりすると、意図しない全件更新や実行エラーにつながることがあります。
「vars 必須にする」「未指定時は直近 N 日」「未指定時は前日のみ」など、運用ルールを先に決めておくのが安全です。

2. 2 つ目以降の条件には T. を付ける

updatePartitionFilter に複数条件を書く場合、Dataform は 1 つ目の条件 に対してテーブルエイリアス T. を自動で補完します。
一方で 2 つ目以降の条件 には補完されません。

そのため、次のようにどちらも date と書くと、生成された MERGE 文の ON 句で 2 つ目の date が曖昧参照になり、実行時エラーになります。

// NG 例: 2 つ目に T. がなくエラーになる
`date >= DATE('...') AND date <= DATE('...')`

// OK 例: 2 つ目以降は T. を明示する
`date >= DATE('...') AND T.date <= DATE('...')`

実際に NG 例で実行すると、次のようなエラーになります。

Query error: Column name date is ambiguous at [****:20:52]

生成 SQL を見ると、1 つ目の条件だけ T.date に補完され、2 つ目は date のまま残っていることが分かります(緑枠が補完済み、赤枠が未補完)。

2 つ目の date に T. がなく ambiguous になる例

一方、2 つ目以降にも T. を明示すると、両方の条件が T.date として解決され、正常に実行できます。

2 つ目以降にも T. を付けた成功例

T(ターゲット)と S(ソース)の両方に date カラムがあるため、エイリアスなしの date はどちらを指すか判別できず失敗します。
複数条件を AND でつなぐときは、2 つ目以降のパーティションカラムに T. を付けるのを忘れないでください。

3. 日付フォーマットと型を合わせる

vars に渡す値は、BigQuery 側で DATE('...') として解釈できる形式(例: YYYY-MM-DD)に揃えます。
パーティションカラムが TIMESTAMP の場合は、DATE() ではなく TIMESTAMP()DATE(timestamp_col) など、型に合わせた式にしてください。

まとめ

  • incremental テーブルの更新範囲は updatePartitionFilter で絞れる
  • 実行ごとに期間を変えたいときは dataform.projectConfig.vars を使う
  • 開始日・終了日のように条件が複数ある場合は、JavaScript で 1 本の SQL 条件文字列 を組み立て、AND でつなぐ
  • 2 つ目以降の条件ではパーティションカラムに T. を明示する
  • vars 未設定時のフォールバックもセットで設計する

updatePartitionFilter 自体は単純な設定ですが、「動的」かつ「複数条件」になると、SQL 文字列をどう組み立てるかがポイントになります。

Cloud Ace Tech Blog
Cloud Ace Tech Blog

Discussion