Dataformのアサーション機能を解説

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

G-gen の min です。Google Cloud のデータ変換パイプラインツールである Dataform で、データ品質テストを実装するためのアサーション(assertion)機能について解説します。

概要

Dataform とは

Dataform は、BigQuery 内で SQL を用いたデータ変換パイプラインを開発、テスト、デプロイ、実行するためのフルマネージドなサービスです。

Dataform の基本的な概念や使い方については、以下の記事を参照してください。

blog.g-gen.co.jp

アサーションとは

Dataform の アサーション は、データ品質テストをパイプラインに組み込むための機能です。アサーションは、内部的には「指定した条件に違反する行を検出する SELECT クエリ」として実装されます。このクエリが 1 行でもデータを返した場合、つまり条件違反のデータが1件でも存在した場合、そのアサーションは失敗となります。

Dataform はワークフローを実行するたびにアサーションを実行します。アサーションが失敗すると、実行ログに失敗が記録され、後続の処理を停止させたり、アラートを送信したりできます。

アサーションの種類

Dataform のアサーションは、大きく2種類に分けられます。

種別 説明 用途
組み込みアサーション テーブル定義ファイル(.sqlx)の config ブロック内で宣言的に指定 一般的なデータ品質チェック
手動アサーション 独立した .sqlx に任意の SQL を記述 複雑なチェック、複数テーブルの整合性

前者の組み込みアサーションは、Dataform に組み込みで用意されたアサーションです。簡単な記述で呼び出すことができます。Dataform には以下の組み込みアサーションが用意されています。

名称 説明
nonNull 指定された列に null の行がないことを確認
rowConditions すべての行が指定した条件を満たすことを確認
uniqueKey 指定した列で、すべての行の値が一意である(重複した値がない)ことを確認
uniqueKeys 指定した列の組み合わせで、すべての行の値が一意である(重複した値がない)ことを確認

後者の手動アサーションは、ユーザーが独自の SQL クエリを記述することによって定義できるアサーションです。記述した SQL が1行でも値を返すと、アサーションは失敗します。

組み込みアサーションの詳細

nonNull

指定したカラムに NULL 値が含まれていないことを検証します。主キーや必須項目など、NULL であってはならないカラムに対して使用します。

以下の例では、user_idemail カラムに NULL 値が存在しないことをテストします。

config {
  type: "table",
  assertions: {
    nonNull: ["user_id", "email"]
  }
}
  
SELECT
  1 AS user_id,
  "test@example.com" AS email

なお、BigQuery テーブルには NOT NULL 制約を指定できますが、この制約と nonNull アサーションを混同しないよう注意が必要です。nonNull アサーションは、データパイプラインの実行時に NULL 値がないかをテストする機能です。このアサーションを定義しても、Dataform が作成する BigQuery テーブルのスキーマ自体に NOT NULL 制約が付与されるわけではありません。

Dataform で自動作成されたテーブルのカラムは、デフォルトですべて NULL を許容する(NULLABLE)モードです。スキーマレベルで NULL 値を確実に禁止したい場合は、Dataform の operations を使用して明示的に設定するなど、追加の対応が必要です。

uniqueKey / uniqueKeys

uniqueKey は、指定した単一カラムの値がテーブル内で一意である(重複がない)ことを検証します。uniqueKeys は、指定した複数カラムの組み合わせがテーブル内で一意であることを検証します。

uniqueKeys では複数のカラムセットをテストできます。以下の例では、「user_id の一意性」と「signup_datecustomer_id の組み合わせの一意性」の2つの条件をテストします。

config {
  type: "table",
  assertions: {
    uniqueKeys: [
      ["user_id"],
      ["signup_date", "customer_id"]
    ]
  }
}
  
SELECT ...

rowConditions

独自の SQL 条件式を記述して、すべての行がその条件を満たすことを検証します。各条件式は文字列として記述し、テーブルのいずれかの行で条件式が false または NULL を返すと、アサーションは失敗します。

以下の例では、「signup_date が NULL でない場合は 2025-10-01 より後である」かつ「email がメールアドレス形式である」という2つの条件をテストします。

config {
  type: "incremental",
  assertions: {
    rowConditions: [
      'signup_date is null or signup_date > "2025-10-01"',
      'REGEXP_CONTAINS(email, r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")'
    ]
  }
}
  
SELECT ...

手動アサーションの詳細

定義方法

手動アサーションは、組み込みアサーションで表現しにくい複雑なロジックや、複数テーブルをまたぐテストに使用します。

手動アサーションを定義するには、config ブロックの type として assertion を指定し、その下にテストしたい条件(違反する行を見つける)の SQL クエリを記述します。

参照整合性のチェック例

例えば、users テーブルと orders テーブルで、存在しない user_idorders テーブルに含まれていないか(参照整合性)をテストする手動アサーションは以下のようになります。このクエリは、orders テーブルに存在する user_idusers テーブルに存在しない行を検出します。

-- definitions/assertions/check_order_user_id.sqlx
  
config {
  type: "assertion"
}
  
SELECT
  o.order_id
FROM
  ${ref("orders")} AS o
LEFT JOIN
  ${ref("users")} AS u
  ON o.user_id = u.user_id
WHERE
  u.user_id IS NULL

集計値のチェック例

手動アサーションは、集計値に対するテストも定義できます。例えば、日別の売上データ(daily_sales)に、マイナスの売上日が存在しないことを確認するアサーションは以下のように記述できます。

-- definitions/assertions/check_negative_revenue.sqlx
  
config {
  type: "assertion"
}
  
SELECT
  sale_date
FROM
  ${ref("daily_sales")}
WHERE
  total_revenue < 0

アサーションの依存関係の制御

すべての依存先アサーションを待機

アサーション失敗時に後続処理を止めたい場合は、依存関係を制御します。

デフォルト設定では、アサーションが失敗しても、そのテーブルを参照する後続の処理は実行されます。 config ブロックで dependOnDependencyAssertions: true を設定すると、このアクション(テーブル作成などの処理)が依存するすべてのアクションに定義されたアサーションが成功するまで、処理の実行は開始されません。

-- table_B.sqlx
  
config {
  type: "table",
  dependOnDependencyAssertions: true
}
  
-- table_A のアサーションがすべて成功しないと、table_B の作成は開始されない
SELECT * FROM ${ref("table_A")}

特定の依存アクションのアサーションのみ待機

dependencies パラメータで依存アクションを指定する際に includeDependentAssertions: true を設定することで、特定の依存アクションのアサーションのみを待機させることができます。

以下の例では、table_Ctable_Atable_B に依存していますが、table_B のアサーションが成功した場合にのみ、処理が実行されます(table_A のアサーション成否は問いません)。

-- table_C.sqlx
  
config {
  type: "table",
  dependencies: [
    "table_A",
    {name: "table_B", includeDependentAssertions: true}
  ]
}
  
SELECT * FROM ${ref("table_A")}
LEFT JOIN ${ref("table_B")} ON ...

特定のアサーションのみを依存先に指定

さらに細かく、特定のアクションの特定の組み込みアサーションや手動アサーションのみを依存関係に設定することもできます。アサーション名は アクション名_assertions_アサーション種別_インデックス という命名規則で付けられています。手動アサーションの場合は、ファイル名がそのままアサーション名になります。

-- table_B.sqlx
  
config {
  type: "table",
  dependencies: [
    "table_A_assertions_uniqueKey_0", -- table_A の uniqueKey アサーション
    "check_order_user_id"             -- 手動アサーション
  ]
}
  
SELECT * FROM ${ref("table_A")}

実行結果の確認と通知

実行結果の確認

Dataform のワークフロー実行ログから、各アサーションが成功したか失敗したかを確認できます。

アサーション失敗時の通知

アサーションが失敗した場合、Dataform は Cloud Logging にログを自動的に出力します。このログをトリガーとして、Cloud Monitoring でログベースのアラートを設定することで、アサーション失敗時に通知を送信できます。

失敗したワークフロー呼び出しに対するアラート設定の詳細は、公式ドキュメントをご参照ください。

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

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