[初心者向け]Dataformでテーブル間の依存関係について調べてみた

Dataformの依存関係について調査しました。Dataformを使用した経験がまだ無い方向けの記事です。
2024.05.31

クラスメソッド株式会社データアナリティクス事業本部所属のニューシロです。
今回はGoogle CloudのサービスであるDataformについて、テーブル間の依存関係について調べてみました。   Dataformを使用した経験がまだ無い方向けの記事です。

前提

Dataformとは

Dataformの紹介についての引用です。

Dataform は、データ アナリストが BigQuery でデータ変換を行う複雑な SQL ワークフローを開発、テスト、バージョン管理、スケジュール設定するためのサービスです。
Dataform を使用すると、データ統合の ELT(抽出、読み込み、変換)プロセスにおけるデータ変換を管理できます。Dataform では、ソースシステムから抽出されたデータを BigQuery に読み込むと、明確に定義されたテスト済みで一連のデータテーブルに変換できます。

よく比較対象に挙がるサービスとしてはdbtがあります。

依存関係とは

Dataformの依存関係についての引用です。

依存関係がある場合、依存オブジェクトの実行は依存関係オブジェクトの実行に依存します。つまり、Dataform は依存関係の後に依存を実行します。依存関係は、依存オブジェクトの SQLX 定義ファイル内で依存関係を宣言することによって定義します。
依存関係の宣言は、SQL ワークフローの依存関係ツリーを構成し、Dataform が SQL ワークフロー オブジェクトを実行する順序を決定します。

今回は実際にテーブルやsqlxファイルを作成し、依存関係について具体的に調べてみました。
依存関係を定義できる対象としてはテーブルの他にカスタムSQLオペレーションやアサーションがあるようですが、今回はテーブル間に限定して調査しました。

本題

今回想定するDataformの実行ケース

ソーステーブルを作成

BigQueryにて、以下のクエリでテーブルを2つ作成します。

source_1.sql

CREATE TABLE `dataform_source.source_1` AS
SELECT "1" AS id, "apple" AS name
UNION ALL
SELECT "2" AS id, "orange" AS name
UNION ALL
SELECT "3" AS id, "banana" AS name;

source_2.sql

CREATE TABLE `dataform_source.source_2` AS
SELECT "4" AS id, "apple" AS name
UNION ALL
SELECT "5" AS id, "orange" AS name;

Dataformを作成

sqlxファイルを2件作成しました。あまり複雑なコードだと見づらいため、なるべく簡潔になるようにしました。

first_table.sqlx

config {
  type: "table",
  schema: "dataform_table"
}

SELECT * FROM ${ref("dataform_source", "source_1")}
UNION ALL
SELECT * FROM ${ref("dataform_source", "source_2")}

second_table.sqlx

config {
  type: "table",
  schema: "dataform_table"
}

SELECT
  name,
  COUNT(*) AS cnt
FROM ${ref("dataform_table", "first_table")}
GROUP BY name

上記コードのように、依存元のテーブルはref()を用いて指定しています。

依存関係のメリット① 依存関係が可視化される

Dataform画面上でCOMPILED GRAPHを押下すると、使用テーブルやsqlxファイルについて図示されます。
ここで依存関係を設定するメリットがわかるのですが、そのためにまずは依存関係を設定せずに図を表示してみましょう。

試しに、first_table.sqlx, second_table.sqlxについてFROM ${ref("dataform_source", "source_1")}FROM dataform_souce.source_1のように、ref()を用いない形で書き換えCOMPILED GRAPHを押下すると以下の図が表示されます(プロジェクト名は伏せてあります)。

ただテーブルが4つ並んだだけの図となります。
しかし、元のコードのようにfirst_table.sqlx, second_table.sqlxについてref()を用いて依存元テーブルを設定することで以下のように表示されます

このように依存関係が可視化され、データフローを簡単に理解することができます。

依存関係のメリット② sqlxファイル実行時に依存関係のあるsqlxファイルも併せて実行できる

BigQueryデータソース、sqlxファイルの説明

使用している2つのBigQueryデータソースと、2つのsqlxファイルについて簡単に説明します。

  • source_1
    • 今回使用するBigQueryデータソース①
+----+--------+
| id |  name  |
+----+--------+
| 1  | apple  |
| 2  | orange |
| 3  | banana |
+----+--------+
  • source_2
    • 今回使用するBigQueryデータソース②
+----+--------+
| id |  name  |
+----+--------+
| 4  | apple  |
| 5  | orange |
+----+--------+
  • first_table.sqlx
    • テーブルsource_1とテーブルsource_2を結合する
SELECT * FROM ${ref("dataform_source", "source_1")}
UNION ALL
SELECT * FROM ${ref("dataform_source", "source_2")}

# 結果
+----+--------+
| id |  name  |
+----+--------+
| 1  | apple  |
| 2  | orange |
| 3  | banana |
| 4  | apple  |
| 5  | orange |
+----+--------+
  • second_table.sqlx
    • first_tableに対してGROUP BY句でnameごとの件数を出力する
SELECT
  name,
  COUNT(*) AS cnt
FROM ${ref("dataform_table", "first_table")}
GROUP BY name

# 結果
+--------+-----+
|  name  | cnt |
+--------+-----+
| apple  |   2 |
| banana |   1 |
| orange |   2 |
+--------+-----+

以上の2つのBigQueryデータソースと、2つのsqlxファイルを使用してDataformを実行します。

Dataformのワークフロー実行

依存関係について確認するため、Dataformのワークフローを実行していきます。 データ処理の流れについて把握するため、source_2にレコードを1件追加します。

INSERT `dataform_source.source_2` (id, name)
VALUES("6", "apple");

# 実行後のsource_2レコード
+----+--------+
| id |  name  |
+----+--------+
| 4  | apple  |
| 5  | orange |
| 6  | apple  |
+----+--------+

この後に、second_table.sqlxを実行してみます。

図から確認できるように、second_table.sqlxの元であるfirst_table自体には変更が無いため、second_table.sqlxを実行してももちろん変化はありません。
しかし、ここで依存関係を利用することができます。まずはDataform画面上で実行するsqlxファイルを選択します。

その後「依存関係を含める」にチェックを入れます。

この状態でsecond_table.sqlxを実行すると、second_table.sqlxだけではなく、依存関係があるfirst_table.sqlxまで実行してくれます。

これにより、source_2に追加した1件のレコードが、first_tableにも反映され、その後second_tableにも反映されます。

実行結果

# second_table.sqlxを実行した結果
# 依存関係を含めないで実行した場合
+--------+-----+
|  name  | cnt |
+--------+-----+
| apple  |   2 |
| banana |   1 |
| orange |   2 |
+--------+-----+

# 依存関係を含めて実行した場合
# appleの数が追加されている
+--------+-----+
|  name  | cnt |
+--------+-----+
| apple  |   3 |
| banana |   1 |
| orange |   2 |
+--------+-----+

このように、実行したsqlxファイル以外にも必要なsqlxファイルを実行してくれます。とても便利ですね。

補足

チェックを入れる画面で、「依存関係を含める」だけでなく、「依存者を含める」「完全に更新して実行する」がありました。

「依存者を含める」にチェックを入れると、「依存関係を含める」とは逆で、実行するsqlxファイルに依存するアクションを実行してくれるようでした。

また、「完全に更新して実行する」にチェックを入れると、増分テーブルの場合でもテーブルを再作成してくれるようです。

最後に感想

今回のようなシンプルな例だと可視化されなくてもそこまで問題は無いですが、テーブル数が増えると、可視化されることによってデータフローへの理解にとても役立つなと思いました。
また、自分が書いたデータフローが目に見えるようになることで作成するモチベーションも上がりそうですね。

引用・参考まとめ