· HARUBOUYA · dataflow · 10 min read

Dataformでデータ変換を管理する

画面に貼り付けたSQLは、誰がいつ何のために変えたのか追えなくなります。SQLをファイルとして持ち、依存関係を自動で解決して実行する仕組みの入り方を書きます。

データ変換の実行状況を映したノートPCの画面

データ基盤を作っていると、SQLが増えます。集計用、加工用、確認用などです。これらが管理画面の「スケジュールされたクエリ」に散らばっていくと、ある時点から誰がいつ何のために変えたのかが追えなくなります。

Dataform は、そのSQLをファイルとして持ち、依存関係を自動で解決して順に実行する仕組みです。BigQuery に組み込まれており、追加の費用はかかりません(実行されるクエリの料金だけかかります)。

問題:画面に貼ったSQLは管理できない

起きること理由
実行順が人の記憶頼りになりますAの結果を使うBを、Aより先に動かしてしまいます
変更の履歴が残りません誰がいつ直したか、なぜ直したかが分かりません
同じ加工が何か所にも書かれます直すときに全部を直せません
テスト用と本番用が混ざります消してよいものが分からなくなります

一番重い症状は実行順です。元のテーブルができる前に集計が動き、前日の数字で集計された結果が静かに残ります。

仕組み

Dataform の構成要素は3つだけです。

要素役割
.sqlx ファイル1ファイル=1テーブル(またはビュー)。SQLと設定を1つに書きます
ref()他のテーブルを参照する書き方。これが依存関係になります
ワークフローの実行依存関係の順に、まとめて実行します

ファイルの中身

config {
  type: "table",
  schema: "mart",
  description: "日別の売上集計"
}

SELECT
  DATE(order_at, "Asia/Tokyo") AS order_date,
  COUNT(DISTINCT order_id) AS order_count,
  SUM(amount) AS amount
FROM
  ${ref("stg_orders")}
GROUP BY
  order_date

config が設定、その下がSQLです。${ref("stg_orders")} の部分が、実際のテーブル名に置き換わります。

type の使い分け

type何になるか使う場面
viewビュー元データが小さいです。常に最新を見たいです
tableテーブル(毎回作り直し)集計結果。件数が数百万までなら扱いやすいです
incrementalテーブル(差分を追加)ログのように積み上がるデータ
declaration既存テーブルの宣言外から取り込まれたテーブルを ref() で使えるようにします

依存関係は書かなくてよい

ref() を使えば、実行順は自動で決まります。

stg_orders  →  mart_daily_sales  →  mart_monthly_sales

上の並びを人が書く必要はありません。mart_daily_sales が ref("stg_orders") を使っている事実から、Dataform が順番を組み立てます。

これが画面に貼ったSQLとの最大の違いです。テーブルを1つ足しても、実行順の設定を直す作業が発生しません。

検査を付ける

Dataform には assertion(検査)があります。条件を満たさない行が1件でもあれば、そこで止まります。

config {
  type: "table",
  schema: "mart",
  assertions: {
    uniqueKey: ["order_date"],
    nonNull: ["order_date", "amount"]
  }
}
検査意味
uniqueKey指定した列の組み合わせが重複していないこと
nonNull指定した列が空でないこと
rowConditions自分で書いた条件を満たすこと(例:amount >= 0)

uniqueKey だけは最初から付けます。集計が二重に走ったときに気づける唯一の仕掛けになります。

Gitにつなぐ

Dataform はリポジトリと接続できます。接続すると次ができます。

できること効果
変更の履歴が残ります誰がいつ何を直したかを追えます
枝を分けて作業できます本番の定義を壊さずに試せます
変更を確認してから反映できますSQLの差分を他の人が見られます

1人で使う場合でも、過去の定義に戻せるという理由だけで接続する価値があります。

やること

手順1. 1テーブルだけ移す

全部を一度に移さないでください。いま動いているスケジュール実行のうち、依存の少ないものを1つ選びます。

  1. Google Cloud コンソールで「BigQuery」→「Dataform」を開きます
  2. 「リポジトリを作成」を押し、名前とリージョンを入れます
  3. 作成したリポジトリを開き、「開発ワークスペースを作成」を押します
  4. definitions フォルダに新しい .sqlx ファイルを作ります
  5. 既存のSQLを貼り、上に config { type: "table", schema: "..." } を足します

手順2. 参照を ref() に置き換える

SQLの中に書かれているテーブル名を探し、${ref("テーブル名")} の形に直します。

元のテーブルが Dataform の外にある場合は、先に declaration を作ります。

config {
  type: "declaration",
  schema: "raw",
  name: "orders"
}

手順3. 実行して確かめる

  1. 画面右上の「実行」を押します
  2. 「アクション」の一覧から、作ったものを選びます
  3. 実行結果が「成功」になることを確認します
  4. BigQuery 側で、できたテーブルの件数を手元の数字と突き合わせます

手順4. スケジュールを設定する

  1. リポジトリの画面で「ワークフロー設定」を開きます
  2. 「作成」を押し、実行する対象と頻度(cron 形式)を指定します
  3. 実行に使うサービスアカウントを選びます

旧いスケジュール実行は、しばらく両方動かしてから止めます。結果が一致することを数日確認してから切り替えます。

失敗例

失敗何が起きるか代わりにやること
画面のSQLとDataformを二重に持ちますどちらが正か分からなくなります移した時点で旧い側を止めます。止めるまでの期間を決めます
ref() を使わずテーブル名を直書き依存関係が認識されず、実行順が崩れます参照はすべて ref() にします
assertion を付けません二重実行や欠損に気づけません最低でも uniqueKey を付けます
incremental の絞り込み条件が甘いです同じ行が何度も追加されます取り込み条件と一意キーを必ず指定します
全部を一度に移しますどこで壊れたか分かりません依存の少ないものから1つずつ
本番のスキーマに直接書きながら開発します検証中のテーブルが利用者に見えます開発用のスキーマを分けます
説明を書きません半年後に用途が分からなくなりますdescription を1行入れます

まとめ

  • 画面に貼ったSQLの一番の問題は、実行順が人の記憶頼りになること
  • Dataform は .sqlx ファイルと ref() だけで、依存関係と実行順を自動で解決します
  • テーブルを足しても、実行順の設定を直す作業が発生しません
  • uniqueKey の検査は最初から付けます。二重実行に気づける唯一の仕掛け
  • Gitにつなぐと、過去の定義に戻せます。1人で使う場合でも価値があります
  • 移行は1テーブルずつ行います。旧いスケジュール実行と数日並走させてから切り替えます

最初にやることは、いま動いているスケジュール実行を一覧にして、依存の少ないものを1つ選ぶことです。そこから始めれば、半日で仕組みの形が分かります。

Share:

参考になったら、いいねを押してください。

コメント

    お名前とコメントは、確認後にこのページで公開されます。メールアドレスや電話番号などは書かないでください。

    Back to Blog

    Related Posts

    View All Posts »