dbt

dbt Model Contracts入門: スキーマと型の契約を正しく使う

2026-04-19
NicheeLab編集部

モデルの出力スキーマが揺れると、下流ダッシュボードやMLパイプラインが壊れます。dbtのModel Contractsは、モデルのカラム構造とデータ型を“契約”として明示し、実装とスキーマの不一致を早期に検知・防止します。

本記事は、dbt公式ドキュメントの安定機能に基づき、書き方・挙動・テストやDB制約との違い・CI実装の勘所を、資格(Analytics Engineer)で問われやすい観点と実務運用の観点から整理します。詳細仕様や最新のアダプタ対応はdbt Docsを参照してください。

Model Contractsの概要と効果範囲

Model Contractsは、モデルの出力スキーマ(カラム名・順序・データ型)をYAMLで宣言し、contract.enforcedを有効化すると、dbtがビルド時にその契約を満たすようSQLを補正(投影・キャスト)し、不一致があれば失敗させます。これにより、上流の変更が下流にサイレントに伝播することを防げます。

主な効能は次の3つです。1) スキーマの固定(ブレイクの早期検知)、2) 型の明示(暗黙キャストの抑止)、3) 変更レビュー容易化(差分がYAMLに現れる)。ただし、データ内容の品質保証はテストやDB制約の領域です。

  • 固定されるもの: カラム名、順序、データ型(アダプタが許す範囲でキャスト)
  • 検知できる不一致: 余分/不足のカラム、期待と異なる型
  • 対象外: 値の妥当性や参照整合性(これはテスト/DB制約の役割)

契約の流れ(開発→CI→実行基盤)

PRdbt buildDeveloper ChangeSQL + YAMLCI (dbt run/build)with contractData WarehouseTable/ViewLocal testdbt build / fix mismatchContract mismatchCI fail (fail fast)Relationfixed schema (cast/project)Developer → CI(契約検証) → Data Warehouse: 不一致はCIで遮断し、成功時にのみスキーマが反映される

契約の書き方(YAMLとモデル設定)

契約はモデルのYAMLファイルでカラム定義と型を宣言し、contract.enforcedを有効にして適用します。データ型は“使用するウェアハウスの型名”で記述します。型エイリアスは多くのアダプタで受理されますが、曖昧な表現は避け、実環境でcompile/build確認を行うのが安全です。

contractはモデル単位、パッケージ単位(dbt_project.ymlのモデル選択器)で有効化できます。組織標準として、上流に消費されるすべての公開モデル(例: marts)で必須にする運用が現実的です。

  • YAMLでcolumns[].nameとcolumns[].data_typeを必ず指定
  • contract.enforced: true をモデル/階層で有効化
  • 型名はアダプタのネイティブ型(Snowflake: NUMBER/VARCHAR、BigQuery: INT64/STRING、Postgres: BIGINT/TEXT など)

例: schema.yml と dbt_project.yml で契約を有効化

version: 2
models:
  - name: dim_customers
    description: 顧客ディメンション(公開対象)
    config:
      contract:
        enforced: true
    columns:
      - name: customer_id
        description: 事業内一意の顧客ID
        data_type: BIGINT   # Snowflake: NUMBER(38,0) などでも可
        tests:
          - not_null
          - unique
      - name: customer_name
        data_type: VARCHAR
      - name: is_active
        data_type: BOOLEAN

# dbt_project.yml(marts配下を既定で契約有効化)
models:
  my_project:
    marts:
      +contract:
        enforced: true

挙動の詳細(型キャスト・カラム順序・不一致時の失敗)

contractが有効な場合、dbtはモデルのSELECTに対し、宣言された順序でカラムを投影し、必要に応じてアダプタが許す範囲で型キャストを挿入します。SELECTに余分なカラムがあっても、契約外は出力から落ちます。欠落カラムや明示的にキャスト不能な型不一致はビルド失敗になります。

ビューでも列の投影とキャストでスキーマは固定されます。テーブル作成時は宣言型でCREATE TABLEが行われ、後段にINSERT/CREATE OR REPLACEが続くのが一般的です(正確なDDLはアダプタに依存)。

  • 余分なカラム: 出力から除外(契約外)
  • 欠落カラム: 失敗(Missing column)
  • キャスト不能: 失敗(例: 'abc' を INT に明示キャスト)
機能主な目的実行タイミング失敗時の挙動
Model Contractsスキーマ(名前・順序・型)の固定dbt build/run のモデル生成時不一致でジョブ失敗。余分列は除外、欠落/非対応キャストはエラー
dbt Tests値品質と関係の検証dbt test 実行時(しばしばCI)テスト失敗をレポート。モデル生成は成功済みのことが多い
DB制約(NOT NULL, PK等)DBレベルの一貫性担保DML/DDL適用時(DBが評価)DBが挿入/更新を拒否しトランザクション失敗

例: SQLモデル内でconfigを直接指定(adapterに応じてキャストが挿入される)

{{ config(materialized='view', contract={'enforced': True}) }}

with src as (
  select * from {{ ref('stg_customers') }}
)
select
  cast(customer_id as bigint)   as customer_id,
  cast(customer_name as varchar) as customer_name,
  cast(active as boolean)        as is_active
from src

マテリアライゼーション別の注意点(テーブル/ビュー/インクリメンタル)

テーブル/ビューは一般に契約適用の第一候補です。ビューでも投影とキャストでスキーマは固定されますが、下層テーブルの型変更が伝播してキャスト不能になればビルド時に失敗します。

インクリメンタルモデルでは、既存テーブルの列追加や型変更はDB側のDDL互換性に依存します。契約で列を追加した場合、必要に応じてfull-refreshで再作成する運用が安全です。

  • ビュー: 契約で列順・型を固定。DDL制約は弱いが安全に反映しやすい
  • テーブル: CREATE TABLEで宣言型を反映。差分変更はDBのALTER可否に依存
  • インクリメンタル: 互換性のないスキーマ変更はfull-refreshを前提に計画

運用ヒント: 変更種別に応じたリリース手順メモ

- 互換追加(列の後方追加): 事前にYAMLへ追加 → ビルド → 下流へ告知
- 互換削除(未使用列の除去): 下流の参照削除 → YAMLから除外 → ビルド
- 非互換(型変更/列名変更): ブランチで下流も同時修正 → CIで一括検証 → 本番はfull-refresh

契約・テスト・DB制約の使い分け

契約は“出力の形”を固定します。値の品質はテストの役割、実データ操作の強制力はDB制約の役割です。3者を重ねると、設計の意図がYAML/DDL/テストに明示化され、変更も可視化されます。

実務では、公開モデルには契約必須、主キー/外部キー相当はテストで担保、可能な範囲でDB制約(NOT NULLや一意制約)を活用、という使い分けが安定します。

  • 契約: スキーマの互換性境界を定義(破壊的変更を遮断)
  • テスト: 値の品質・関係(not_null, unique, relationships 等)
  • DB制約: 実データ操作の最後の砦(アダプタ/エディション差に注意)

例: 契約とテストの併用(relationshipsで参照整合性を担保)

version: 2
models:
  - name: fct_orders
    config:
      contract:
        enforced: true
    columns:
      - name: order_id
        data_type: BIGINT
        tests: [unique, not_null]
      - name: customer_id
        data_type: BIGINT
        tests:
          - not_null
          - relationships:
              to: ref('dim_customers')
              field: customer_id
      - name: order_total
        data_type: NUMERIC

CI組み込みとガバナンス(試験にも出る実務ポイント)

PRでdbt buildを走らせ、契約不一致をブロックするフローを必須化します。モデルの契約変更(列追加・型変更・削除)は必ずPRでYAML差分が見えるため、レビューしやすくなります。カタログ/ドキュメント生成と合わせると、APIライクな公開契約を実現できます。

ブレイク検知を強くするには、公開階層(marts)をターゲットに選択実行、seed/sourceのスキーマ変更検知、full-refreshの定期実施(インクリメンタルの負債解消)を組み合わせます。

  • CIでは dbt deps → dbt build -s state:modified+ を基本線に
  • 契約変更はCHANGELOG化し、下流へリリースノート配信
  • カバレッジ指標: 契約適用モデル比率、テストカバレッジ、CI失敗率

例: GitHub ActionsでのラフなCIステップ

steps:
  - run: dbt deps
  - run: dbt build -s state:modified+ --profiles-dir .
  - run: dbt test -s tag:public
  # 契約不一致やテスト失敗でPRをブロック

問題で確認

Analytics Engineer

問題 1

公開モデルdim_customersに新しい列customer_tier(文字列型)を追加したい。下流の互換性を維持しつつ、dbt Model Contractsを使って安全にリリースする最も適切な手順はどれか?

  1. schema.ymlにcustomer_tier列(適切なdata_type)を後方に追加し、PRでCIのdbt buildを通したうえで本番反映する
  2. モデルSQLにcustomer_tierをSELECTへ追加するだけでよい。契約は自動で更新される
  3. dbt testだけを実行し、失敗がなければ本番に反映する(契約は後で更新)
  4. 契約を一時的に無効化してデプロイし、安定後に再度有効化する

正解: A

契約はYAMLで宣言されたカラムが真実。先にschema.ymlへ適切なdata_typeで列を追加し、CIで契約整合と型キャストの成立を検証してからリリースするのが安全。SQLだけの変更や契約の後回しは、下流互換性の見落としにつながる。

よくある質問

data_typeはdbtの共通型名でも書けますか?

いいえ。基本は使用するウェアハウスのネイティブ型名で指定します。多くのアダプタはエイリアスを受理しますが、曖昧さを避けるため実環境でdbt buildを行い、生成DDLとキャストを確認してください(参照: dbt Docs)。

契約があるのに余分なカラムがSELECTに入っていても通るのはなぜ?

契約有効時は、宣言されたカラムのみを順序通りに投影するため、契約外のカラムは出力から落とされます。これは下流の互換性維持のための仕様です。一方で、欠落カラムや不可能な型キャストはビルド失敗となります。

インクリメンタルモデルで型変更はどう扱うべきですか?

既存テーブルの型変更はDBのALTER互換性に依存し、運用リスクが高いです。契約を更新した上で、原則としてfull-refreshで再作成する計画を取り、必要に応じて移行期間を設けましょう。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
dbt

dbt Model の基礎: SQL で定義する変換の最小単位

Analytics Engineer 向けに、dbt Model の定義、マテリアライゼーション、依存関係、インクリメン...

dbt

dbt Analytics Engineer 試験ガイド: 出題範囲・配点・申込の実務視点

dbt Analytics Engineer 認定の出題範囲、配点の考え方、申込から受験までの流れを、公式ドキュメントの...

dbt

dbt Cloud と dbt Core の違いと選び方:Analytics Engineer 試験に効く要点

dbt Cloud と dbt Core の機能差を、実務と資格対策の両面から整理。スケジューリング、IDE、RBAC、...

dbt

dbt プロジェクト構造ガイド: models / seeds / macros の実務レイアウト

Analytics Engineer 向けに、dbt プロジェクトのディレクトリ構造と命名規約、dbt_project....

dbt

dbt_project.yml の読み方:主要設定と命名を最短で掴む

dbt_project.yml の必須キー、命名解決(database.schema.identifier)、設定優先度...

dbtの記事一覧 (100件)
© 2026 NicheeLab All rights reserved.