Dataformのテーブル定義と組み込み関数の使い方

記事タイトルとURLをコピーする

G-gen の min です。データ変換パイプラインツールである Dataform における、SQLXファイルにおけるテーブル定義と、その中で使用される組み込み関数について解説します。

Dataform と SQLX

Dataform は、Google Cloud 上でデータ変換のワークフローを開発、テスト、デプロイ、スケジューリングするためのサービスです。データアナリストやエンジニアは、SQL を用いて BigQuery 内にテーブルやビューを効率的に作成・管理できます。

Dataform の中核となるのが SQLX というファイル形式です。SQLX ファイルは、標準的な SQL に加えて、Dataform 独自の機能を追加したものです。具体的には、ファイルの先頭に config ブロックと呼ばれる設定ブロックを記述し、その後に SELECT 文を記述することで、テーブルやビューの定義とデータの中身を一度に管理できます。

以下の記事では Dataform の基本的な概念を解説しています。あわせてご参照ください。

blog.g-gen.co.jp

テーブル定義の基本

SQLX ファイルで新しいテーブルを定義する際の基本的な構造は、以下のようになります。

config {
    type: "table",
    schema: "my_dataset",
    name: "my_new_table"
}

SELECT
  'hello' AS greeting,
  CURRENT_TIMESTAMP() AS created_at

config ブロックでは、作成するアセットのタイプtableviewincremental など)や、出力先のスキーマ(BigQuery のデータセット名)、テーブル名などを指定します。config ブロックに続く SELECT 文が、そのテーブルの定義となります。

この SQLX ファイルを Dataform が実行すると、BigQuery の my_dataset データセット内に my_new_table という名前のテーブルが作成されます。

*  参考 : テーブルを作成する

主要な組み込み関数

Dataform の SQLX ファイルでは、config ブロックや SELECT 文の中で、ref()self() といった組み込み関数を使用できます。これらの関数は、Dataform が SQL をコンパイルして BigQuery で実行する際に、実際のテーブル名やスキーマ名に自動的に置き換えられます。

これにより、環境(開発/本番)ごとに出力先を変更したり、テーブル間の依存関係を明示的に定義したりすることが容易になります。

*  参考 : Dataform コアのリファレンス

主要な組み込み関数の概要を、以下の表にまとめます。

関数名 主な用途 参照対象 返り値の例
ref() Dataform ワークスペース内の他のテーブル/ビューを参照し、依存関係を定義する ワークスペース内の他の SQLX ファイル my-project.my_dataset.my_table
resolve() Dataform 管理外の BigQuery 上の既存テーブル/ビューを参照する BigQuery 上の任意のテーブル/ビュー my-project.another_dataset.external_table
self() 増分テーブルなどで自分自身のテーブルを参照する 現在の SQLX ファイルが生成するテーブル my-project.my_dataset.this_table
name() 生成されるテーブル/ビュー名を取得する 現在の SQLX ファイルのファイル名または configname this_table
schema() 生成されるテーブル/ビューのスキーマ名(データセット名)を取得する configschema または workflow_settings.yamldefaultDataset my_dataset
database() 生成されるテーブル/ビューのデータベース名(プロジェクトID)を取得する workflow_settings.yamldefaultProject my-project

組み込み関数の解説

ref()

ref() 関数は、Dataform ワークスペース内の他のテーブルやビューを参照するために使用する最も基本的な関数です。

ref() 関数の引数には、参照したい SQLX ファイルのファイル名を指定します。

例えば source_customers.sqlx というファイルで定義されたテーブルを参照したい場合、以下のように記述します。

SELECT
  *
FROM
  ${ref("source_customers")}

Dataform は SQL コードをコンパイルする際に ${ref("source_customers")} の部分を、source_customers.sqlx で定義されたテーブルの完全な名前(例: my-project.my_dataset.customers)に置き換えます。

ref() を使用する最大のメリットは、テーブル間の依存関係が自動的に解決されることです。Dataform は ref() の記述を基に依存関係グラフ(DAG)を構築し、参照元のテーブルが作成された後で参照先のテーブルを作成するように、実行順序を自動で制御します。

resolve()

resolve() 関数は、Dataform 側で依存関係を追跡する必要がない、BigQuery 上の既存のテーブルやビューを参照するために使用します。

ref() が Dataform ワークスペース内で定義されたアセットを参照し、依存関係を自動で管理するのに対し、resolve() は Dataform の管理外にある BigQuery 上のテーブルなどを参照し、依存関係を追加しません。

resolve() の引数には、スキーマ(データセット)名とテーブル名を指定します。

  -- "raw_data" データセットの "sales_2025" テーブルを参照
SELECT
  *
FROM
  ${resolve("raw_data",
    "sales_2025")}

コンパイル時、この部分は my-project.raw_data.sales_2025 のような完全なテーブル名に置き換えられます。

resolve() で参照したテーブルは、Dataform の依存関係グラフには含まれません。 そのため、Dataform は resolve() で参照されるテーブルの存在や作成順序を管理しません。

self()

self() 関数は、自分自身のテーブル名を完全な形式で参照するために使用します。 引数は不要です。

この関数は、特に type: "incremental" を指定した増分テーブルを定義する際に非常に役立ちます。増分テーブルでは、前回の実行で作成した自分自身のテーブルから最新のデータを取得し、新しく追加されたデータのみを追記する、といった処理を記述するために使用します。

この関数は、テーブル定義のメインとなる SELECT 文の実行前に処理を追加する pre_operations ブロック内で特に有効です。

config {
    type: "incremental"
}

  -- pre_operations は、メインのSELECT文の実行前に実行されるSQLブロック
SELECT
  *
FROM
  ${ref("source_sessions")}

pre_operations {
  -- when(incremental(), ...) は増分実行時のみ中のSQLを実行する構文
    ${
        when(incremental(),
            `DELETE FROM ${self()} WHERE session_date IN (SELECT DISTINCT session_date FROM ${self()})`)
    }
}

上記の例では、pre_operations ブロック内で、これから挿入するデータと同じ日付の既存データを一度削除するために self() を使用しています。self() は、この SQLX ファイル自身が作成するテーブルの完全な名前(例: my-project.my_dataset.this_table)に展開されます。

name()

name() 関数は、この SQLX ファイルから生成されるテーブル名を返します。具体的には、SQLX ファイルのファイル名(拡張子を除く)が返されます。例えば、user_summary.sqlx というファイル内で ${name()} を使用すると、user_summary という文字列が返されます。

config ブロックで name プロパティを指定してテーブル名を明示的に上書きした場合、name() はその上書きされた名前を返します。

schema()

schema() 関数は、この SQLX ファイルから生成されるアセットのスキーマ(データセット)名を返します。

この値は、config ブロックの schema プロパティで指定された値が返されます。

database()

database() 関数は、この SQLX ファイルがターゲットとするデータベース名を返します。Google Cloud のコンテキストでは、これは Google Cloud プロジェクト ID に相当します。

関数の使い分けと実践

workflow_settings.yaml の設定例

前掲の関数を組み合わせることで、柔軟で再利用性の高いデータパイプラインを構築できます。

例えば、workflow_settings.yaml で開発環境と本番環境の変数を定義し、出力先を切り替える構成がよく用いられます。

defaultProject: my-project
defaultLocation: asia-northeast1
defaultDataset: dataform
defaultAssertionDataset: dataform_assertions
dataformCoreVersion: 3.0.0

この設定ファイルを基に、次に示すような SQLX ファイルを記述できます。

stg_users.sqlx

config {
    type: "view",
    schema: "dwh_stg",
    name: "users"
}

SELECT
  user_id,
  user_name,
  register_date
FROM
  ${
      resolve("raw_data",
          "users_source")
  }

dim_users.sqlx

config {
    type: "table",
    schema: "dwh_mart",
    name: "dim_users_summary"
}

  -- Dataformで作成した中間ビュー (stg_users) を参照
SELECT
  register_date,
  COUNT(DISTINCT user_id) AS new_users
FROM
  ${ref("stg_users")}
GROUP BY
  register_date

展開結果

このワークスペースをコンパイルすると、各関数は以下のように展開されます。

  • stg_users.sqlx 内の ${resolve("raw_data", "users_source")}my-project.raw_data.users_source になります。
  • dim_users.sqlx 内の ${ref("stg_users")}my-project.dwh_stg.users になります(stg_users.sqlxconfig で定義したスキーマと名前が反映されます)。
  • dim_users.sqlx の実行時、もし self() を使えば my-project.dwh_mart.dim_users_summary を参照します。
  • 各ファイル内で ${database()}my-project を、${schema()} はそれぞれの config ブロックで指定されたスキーマ名(dwh_stg または dwh_mart)を返します。

このように組み込み関数を適切に使用することで、SQL コード内にプロジェクト ID やデータセット名をハードコーディングすることなく、依存関係が明確で保守性の高いデータパイプラインを構築できます。

佐々木 愛美 (min) (記事一覧)

クラウドソリューション部 データアナリティクス課。2024年7月 G-gen にジョイン。G-gen 最南端、沖縄県在住。最近覚えた島言葉は、「マヤー(猫)」。