こんにちは、SCSKの齋藤です。
本記事では、Google CloudのDataformを使ったデータ品質チェック(アサーション)の実装についてご紹介します。
はじめに
BigQueryにデータを格納するパイプラインを構築していると、「データは入っているけど中身は正しいのか?」という疑問に必ず突き当たります。重複データが入っていないか、必須カラムにNULLが入っていないか、値が想定範囲内かなど、データ品質のチェックは地味ですが大事だなと感じました。
最初は手動でBigQueryのクエリエディタからSELECT文を叩いて確認していましたが、テーブルが増えてくると毎回手動で確認するのは現実的ではありません。チェックを自動化して、問題があれば通知が飛ぶ仕組みが欲しいと考えました。
Google CloudにはDataformというデータ変換・パイプライン管理のサービスがあり、その中に「アサーション」というデータ品質チェックの仕組みが用意されています。SQLを書くだけでチェックを定義でき、テーブル間の依存関係も自動管理してくれます。検証環境で実際にアサーションを作成してみたので、その仕組みと工夫点を紹介します。

Dataformとは
Dataformは、Google Cloudが提供するデータ変換・パイプライン管理のサービスです。
Dataformの主な機能は、SQLXファイル(SQLの拡張構文)を使ったデータ変換パイプラインの定義と実行です。テーブルの作成、ビューの作成、インクリメンタルテーブルの更新などをSQLX形式で定義し、依存関係を自動的に解決して適切な順序で実行してくれます。dbt(data build tool)に似たコンセプトですが、Google Cloudにネイティブに統合されている点が特徴です。
Dataformの「アサーション」機能は、データ品質チェックに特化した機能です。「このSQLクエリの結果が0行であること」を確認するシンプルな仕組みですが、これだけで重複チェック、NULLチェック、範囲チェック、参照整合性チェックなど様々なパターンに対応できます。アサーションが失敗(1行以上返す)した場合、Dataformの実行結果としてエラーが記録され、Cloud Monitoringと連携して通知を送ることも可能です。
${ref()} 関数を使うことで、アサーションが参照するテーブルとの依存関係が自動管理されます。テーブルの更新が完了した後にアサーションが実行されることが保証されるため、実行順序を手動で制御する必要がありません。
アサーションの基本的な考え方
Dataformのアサーションは「問題のあるレコードを返すSQL」を書きます。結果が0行であれば問題なし(テスト成功)、1行以上であれば問題あり(テスト失敗)という判定です。つまり「こういうデータがあってはいけない」という条件をSQLで表現する形になります。
この「逆転の発想」に最初は戸惑いましたが、慣れるとシンプルで書きやすいことがわかりました。「正常であること」を確認するのではなく「異常なレコードを見つける」という方向でSQLを書くので、WHERE句に異常条件を書くだけです。
以下に代表的なアサーションパターンをまとめます。
| チェック種別 | 目的 | SQLのアプローチ |
|---|---|---|
| 重複チェック | 同一キーのデータが複数行ないか | GROUP BY + HAVING count > 1 |
| NULLチェック | 必須カラムにNULLがないか | WHERE col IS NULL |
| 範囲チェック | 値が想定範囲内か | WHERE value < 0 等 |
| 参照整合性 | 外部キーが存在するか | LEFT JOIN + WHERE ref IS NULL |
| 件数チェック | 期待件数が揃っているか | GROUP BY date HAVING count != expected |
実装したアサーションのパターン
重複チェック
同じキーの組み合わせで複数行存在しないかを確認します。
config {
type: "assertion",
description: "データに重複がないことを確認",
tags: ["quality_check"]
}
SELECT id, date_col, COUNT(*) AS cnt
FROM ${ref("my_table")}
WHERE date_col >= DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH)
GROUP BY id, date_col
HAVING cnt > 1
GROUP BYして HAVING count > 1 で重複レコードだけを抽出します。結果が0行であれば重複なし=テスト成功です。
NULLチェック
必須カラムにNULLが入っていないかを確認します。
config {
type: "assertion",
description: "必須カラムにNULLがないことを確認"
}
SELECT *
FROM ${ref("my_table")}
WHERE id IS NULL OR value IS NULL
WHERE句で対象カラムがNULLである行を抽出し、存在しなければテスト成功です。
範囲チェック
数値カラムが想定範囲内かを確認します。例えば「数値が負の値になっていないこと」のような条件です。ビジネスロジック上ありえない値が入っていないかを検証するのに使います。
アサーションの限界
使ってみて感じた限界もあります。
まず、行の有無(0行か1行以上か)でしか判定できないため、「先週の平均値から±10%以内か」のような統計的なしきい値判定は、集計ロジックを自分でSQLに書く必要があります。組み込みの統計チェック機能はありません。
また、あくまで「検知」であって「防止」ではない点も意識しておいたほうがよさそうです。アサーションが失敗してもデータは自動修正されず、書き込みを事前にブロックする仕組みでもありません。あくまで事後チェックです。
さらに、カラムの型やNULL許容といったスキーマレベルの検証には向いていません。それはBigQueryのテーブルスキーマ定義自体で担保するものだと思います。同じチェックパターンを複数テーブルに使い回したい場合も、dbtのようなマクロ機能はないので、DataformのJavaScriptテンプレート機能で自作する必要があります。
工夫したところ・ハマったところ
チェック対象期間を絞らないとコストが跳ね上がる
最初に全期間に対してアサーションを走らせたところ、1回の実行で大量のデータスキャンが発生しました。BigQueryは従量課金なので、大きなテーブルに対して毎日フルスキャンすると無視できないコストになります。WHERE句で直近のデータだけに絞ることで、スキャン量を大幅に削減しました。どの期間をチェックすればビジネス上十分かをチームで合意しておくのが大事だと感じました。
tagsによる分類と選択実行
アサーションにはtags(タグ)を付けることができます。タグを使うことで、「このカテゴリのアサーションだけ実行」「日次で回すものと週次で回すものを分ける」といった運用が可能になります。アサーションが増えてくると実行時間もコストも増えるため、頻度の高いものと低いものを分離しておくのが運用上便利です。
アサーション失敗時の対応フロー
アサーションが失敗しても、データが自動的に修正されるわけではありません。あくまで「異常を検知する」仕組みです。そのためアサーションの description に「何が異常で、次に何を確認するか」を明記しておくことが大事だと感じました。深夜にアラートが来たとき、descriptionを見れば何をすればいいかわかる状態が理想です。
まとめ
– Dataformのアサーションは「問題のあるレコードを返すSQL」を書くだけでデータ品質チェックを自動化できるシンプルかつ強力な仕組み
– ${ref()} で依存関係が自動管理されるため、テーブル更新後に確実にチェックが走ることが保証される
– チェック対象期間を絞ることはBigQueryのコスト管理上必須。全期間フルスキャンは避けるべき
– アサーションの description に「次に何をするか」を書いておくと運用時に助かる
– 後付けで追加できるため、段階的にデータ品質チェックを充実させていける
