dbtのtestを介してSnowflakeの各制約を自動的に適用できるpackage「dbt_constraints」を試してみた #dbt #SnowflakeDB

2023.03.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

さがらです。

dbtのtestを介してSnowflakeの各制約を自動的に適用できるpackage、「dbt_constraints」を試してみたので本記事でその内容をまとめてみます。

dbt_constraintsとは

dbt_constraintsに関する情報は、下記ページにまとまっております。

このページの説明を見ると、

This package generates database constraints based on the tests in a dbt project. It is currently compatible with Snowflake, PostgreSQL, and Oracle only.
(DeepL翻訳) 本パッケージは、dbtプロジェクト内のテストに基づいてデータベース制約を生成します。現在、Snowflake、PostgreSQL、Oracleのみと互換性があります。

…ということで、dbtのtestの内容に応じて各制約を自動で生成し適用してくれるpackageとなっております。

改めて、Snowflakeでは以下の4種類の制約をサポートしています。(Snowflakeの制約に関する公式Docはこちら

  • UNIQUE
  • PRIMARY KEY
  • FOREIGN KEY
  • NOT NULL

一方で、dbtのGeneric testsは以下の4種類となっております。(dbtのGeneric testsに関する公式Docはこちら

  • uniqueUNIQUE制約に該当するテスト
  • not_nullNOT NULL制約に該当するテスト
  • accepted_values:該当する制約なし
  • relationshipsFOREIGN KEY制約に該当するテスト

こう見ると、「PRIMARY KEY制約はどうやって適用するの…?」となってしまいますが、実はdbt_constraintsは、dbt_constraints.primary_keydbt_constraints.unique_keydbt_constraints.foreign_keyという3種類のテストも提供してくれています!そのため、PRIMARY KEY制約はdbt_constraints.primary_keyテストを定義すればOKです。

詳細は後述しますが、dbtに元々備わっているデフォルトのGeneric testsを用いても、dbt_constraintsに含まれるテストを用いても、各制約を自動的に適用することが出来るのです。既存のGeneric testsの記述を書き換えなくても良いのが便利ですね!

Snowflakeで制約を適用するメリット

ただ、Snowflakeの制約と聞いて、「Snowflakeの制約って、NOT NULL制約以外強制しないから意味ないんじゃないの?」と思ってしまう方もいるかもしれませんが、そんなことはありません!

例えば、こちらのドキュメントにある「RELY制約プロパティ」を設定すると、クエリの内容から必要最低限のテーブルJOINを行うように処理を最適化してくれます。

下記は私が以前試した内容ですが、関係するテーブルをすべてJOINしたView(俗に大福帳ともいう)をTableauから参照したとき、選択したフィールドに応じて必要最低限のテーブルだけがJOINされることを確認しています。

また、BIツール上でJOINを定義するときFOREIGN KEY制約に関するメタデータをSnowflakeから抽出し、JOINを行うときに結合キーを自動で適用してくれることもあります。(詳細はこちらの公式Docを。)

そのため、Snowflakeの通常のテーブルであっても制約を適用するメリットはあると私は考えています。

注意点:本packageの運用保守について

こちらのdbt_constraintsについて、リポジトリはSnowflake Labs上にあるため一見Snowflake社公式が管理しているdbt packageと思ってしまいそうですが、このdbt packageはSnowflake社のSenior Solutions ArchitectであるDan Flippo氏を中心にコミュニティを介して開発されたものです。

そのため、何かしら予期せぬエラーが発生したり機能要望をしたい場合は、直接下記のGitHubのリポジトリに対してIssueを立てましょう!

試してみた

jaffle_shop_metricsのリポジトリをforkして、すべてのmodelからtableが生成されるようにdbt_project.ymlを変更した上で、試していきます。(当たり前ですが、Viewに対しては制約をかけることが出来ないためです。)

dbt_constraintsのインストール

まず、dbt_constraintsを対象のdbtプロジェクトに対してインストールします。

dbt_project.ymlと同じ階層に、packages.ymlを定義し、以下の内容を記述します。すでにpackages.ymlがある場合は、下記の内容の- package:以降を追記してください。(dbt_constraintsの最新バージョンは、都度公式から確認するようにしてください。)

記述を終えてファイルを保存したら、dbt depsコマンドでインストールしましょう。

packages:
  - package: Snowflake-Labs/dbt_constraints
    version: [">=0.6.0", "<0.7.0"]

dbt_constraintsのテストを用いて制約を適用

まず、dbt_constraintsに含まれるテストを用いて制約を適用してみます。

jaffle_shop_metricsstg_orders.ymlの中身を以下に書き換えてみます。

dbt_constraints.primary_keyは対象のカラムがPRIMARY KEYであるかを確認するテスト、dbt_constraints.foreign_keyは対象のカラムの値がすべて指定したモデルのフィールドに存在しているか(FOREIGN KEYであるか)を確認するテストです。

version: 2

models:
  - name: stg_orders
    columns:
      - name: order_id
        tests:
          - dbt_constraints.primary_key
      - name: customer_id
        tests:
          - dbt_constraints.foreign_key:
              to: ref('stg_customers')
              field: customer_id

この上で一度dbt buildコマンドを実行し、オブジェクト生成とテストを行います。

コマンド実行後にSnowflake上でstg_ordersテーブルを確認すると、order_idカラムにはPRIMARY KEY制約と必要条件であるNOT NULL制約、customer_idカラムにはFOREIGN KEY制約、が適用されていました!

ちなみに、dbtで生成されるドキュメントからorder_idカラムについて見てみると、Custom Testの記載があり展開すると、dbt_constraints.primary_keyでテストされていることがわかるようになっていました。

dbtデフォルトのGeneric testsから制約を適用

続いてdbtデフォルトのGeneric Testを用いて、自動的に制約が適用されるか確認してみます。

jaffle_shop_metricsstg_orders.ymlの中身を以下に書き換えて、uniquenot_nullrelationshipsのテストが行われるようにします。

PRIMARY KEY制約を追加したい場合は、dbt_constraints.primary_keyでテストする必要があるためご注意ください。

version: 2

models:
  - name: stg_orders
    columns:
      - name: order_id
        tests:
          - unique
          - not_null
          - relationships:
              to: ref('stg_customers')
              field: customer_id

この上で一度dbt buildコマンドを実行し、オブジェクト生成とテストを行います。

コマンド実行後にSnowflake上でstg_ordersテーブルを確認すると、UNIQUE制約、FOREIGN KEY制約、NOT NULL制約、が適用されていました!

これで既存のGeneric testsを書き換えなくても問題ないことが確認できました。強いて言えば、PRIMARY KEY制約はdbt_constraintsのdbt_constraints.primary_keyテストを使う必要があるので、ここだけ注意ですね。

最後に

dbtのtestを介してSnowflakeの各制約を自動的に適用できるpackage「dbt_constraints」を試してみました。

本packageを用いてテストを定義するだけで、各制約とRELY制約プロパティを適用でき、クエリのJOIN処理の最適化を図ることができます。積極的に使っていきましょう!