はじめに
dbt (data build tool) の学習を始めましたが、dbt特有の機能について理解不足な部分があると感じたため、本記事ではdbt特有の seed機能 と ref機能、source機能についてまとめてみました。
この記事で学べること:
- seed機能の使い方と実用例
- ref機能の仕組みと重要性
- source機能の仕組みと重要性
想定読者:
- SQLの基本的な知識がある方
- dbtを学び始めたばかりの方
1. dbtとは?
1.1 dbtの位置づけ
dbt (data build tool) は、データウェアハウス内でのデータ変換を SQL で行うためのツールです。
**ELTにおけるT(tらんsfろ## はじめに
dbt (data build tool) の学習を始めましたが、dbt特有の機能について理解不足な部分があると感じたため、本記事ではdbt特有の seed機能 と ref機能、source機能についてまとめてみました。
この記事で学べること:
- seed機能の使い方と実用例
- ref機能の仕組みと重要性
- source機能の仕組みと重要性
想定読者:
- SQLの基本的な知識がある方
- dbtを学び始めたばかりの方
1. dbtとは?
1.1 dbtの位置づけ
dbt (data build tool) は、データウェアハウス内でのデータ変換を SQL で行うためのツールです。
また、ELTアーキテクチャが前提のツールであり、以下のようなデータパイプラインでの利用が想定されている。
生データ(RDS、S3など)
↓
データウェアハウス(BigQuery、Snowflake、Redshiftなど)
↓
【dbt】← ここでSQLを使ってデータ変換
↓
分析用データ(クリーンで使いやすいテーブル)
1.2 従来のSQLとの違い
| 項目 | 従来のSQL | dbt |
|---|---|---|
| 実行方法 | クエリツールで手動実行 |
dbt runコマンド一つで実行 |
| 依存関係管理 | 手動で順序を管理 | 自動で依存関係を解決 |
| テーブル参照 | テーブル名を直接指定 |
ref()関数で参照 |
| テスト | 手動確認 | 自動テスト機能あり |
| バージョン管理 | 困難 | Gitで簡単に管理 |
2. seed機能:「CSVをアップロードするだけ?」
2.1 seed機能とは
seed機能は、CSVファイルをデータウェアハウスのテーブルとして読み込む機能です。
# seedファイルの配置場所
project/
├── dbt_project.yml
└── seeds/
└── country_codes.csv # ← CSVファイルをここに配置
# CSVをテーブルとして読み込む
dbt seed
2.2 seed機能のメリット
メリット1:バージョン管理が容易
従来の方法の問題点
- SQLファイルを手動で管理
- 変更履歴が追いにくい
- チーム共有が困難
seedを使った場合
seeds/country_codes.csv
↓ Gitで管理
↓ チーム全体で同じデータを共有
↓ 変更履歴を自動追跡
メリット2:データの更新が簡単
従来の方法
-- データ更新のたびにDELETE→INSERT
DELETE FROM country_codes WHERE code = 'JP';
INSERT INTO country_codes VALUES ('JP', '日本');
seedを使った場合
# CSVファイルを編集
# コマンド一つで更新完了
dbt seed --full-refresh
メリット3:環境間でのデータ同期
従来の方法の問題点
開発環境と本番環境でマスタデータが異なる
→ 手動で同期する必要がある
→ 同期漏れのリスク
seedを使った場合
# 同じCSVファイルを使って、どの環境でも同じテーブルを作成
dbt seed # 開発環境
dbt seed --target prod # 本番環境
2.3 seed機能の実用例
例1:国コードマスタ
seeds/country_codes.csv
code,name,region
JP,Japan,Asia
US,United States,North America
CN,China,Asia
DE,Germany,Europe
seedの実行
dbt seed
結果
-- データウェアハウスに自動的にテーブルが作成される
SELECT * FROM country_codes;
-- code | name | region
-- -----|---------------|---------------
-- JP | Japan | Asia
-- US | United States | North America
-- CN | China | Asia
-- DE | Germany | Europe
例2:カテゴリマスタ
seeds/product_categories.csv
category_id,category_name,parent_category_id
1,Electronics,NULL
2,Computers,1
3,Smartphones,1
4,Laptops,2
5,Tablets,2
seedの実行
dbt seed
dbtモデルでの利用
-- models/products_with_category.sql
SELECT
p.product_id,
p.product_name,
c.category_name,
c.parent_category_id
FROM {{ ref('products') }} AS p
LEFT JOIN {{ ref('product_categories') }} AS c
ON p.category_id = c.category_id
ここで登場した {{ ref() }} については、次の章で詳しく解説します。
2.4 seedの注意点
注意点1:大きなデータには向かない
推奨
- マスタデータ(国コード、カテゴリなど)
- 設定ファイル的なデータ
- テスト用のサンプルデータ
非推奨
- トランザクションデータ(大量の売上データなど)
- 頻繁に更新されるデータ
seedは数千行程度のデータに適しています。数万行を超える場合は、別の方法(ELTツールなど)を検討しましょう。
3. ref機能:「なぜテーブル名を直接書かないの?」
3.1 ref機能とは
ref機能は、dbtモデル間で テーブルを参照する ための関数です。
従来のSQL
-- テーブル名を直接指定
SELECT * FROM my_database.my_schema.users;
dbtのref関数
-- ref関数で参照
SELECT * FROM {{ ref('users') }};
ref関数の基本的な使い方
1. 基本構文
{{ ref('モデル名') }}
2. 実際の使用例
-- models/stg_users.sqlというファイルがある場合
SELECT * FROM {{ ref('stg_users') }}
-- JOINでの使用
SELECT
u.user_id,
o.order_id
FROM {{ ref('stg_users') }} AS u
LEFT JOIN {{ ref('stg_orders') }} AS o
ON u.user_id = o.user_id
3. ref関数が実際に生成するSQL
-- dbt run実行時に以下のように変換される
SELECT * FROM dev_database.dbt_schema.stg_users
-- 本番環境では
SELECT * FROM prod_database.dbt_schema.stg_users
重要なポイント
-
ref()の引数はファイル名(拡張子なし) - データベース名やスキーマ名は不要
- 環境ごとの設定は
dbt_project.ymlで管理
3.2 ref機能のメリット
メリット1:依存関係の自動管理
従来のSQLの問題点
-- ステップ1:usersテーブルを作成
CREATE TABLE users AS
SELECT * FROM raw_users;
-- ステップ2:ordersテーブルを作成
CREATE TABLE orders AS
SELECT * FROM raw_orders;
-- ステップ3:user_ordersを作成(usersとordersに依存)
CREATE TABLE user_orders AS
SELECT
u.user_id,
u.name,
o.order_id,
o.amount
FROM users AS u
LEFT JOIN orders AS o
ON u.user_id = o.user_id;
問題
- 実行順序を 手動で管理 する必要がある
- 順序を間違えるとエラーが発生
- テーブルが増えると管理が困難
dbtのref関数を使った場合
models/staging/stg_users.sql
SELECT * FROM {{ source('raw_data', 'users') }}
models/staging/stg_orders.sql
SELECT * FROM {{ source('raw_data', 'orders') }}
models/marts/user_orders.sql
SELECT
u.user_id,
u.name,
o.order_id,
o.amount
FROM {{ ref('stg_users') }} AS u
LEFT JOIN {{ ref('stg_orders') }} AS o
ON u.user_id = o.user_id
実行
# dbtが自動的に依存関係を解決して、正しい順序で実行
dbt run
# 実行順序(自動)
# 1. stg_users
# 2. stg_orders
# 3. user_orders
依存関係の可視化
# 依存関係グラフを生成
dbt docs generate
dbt docs serve
メリット2:環境に依存しない参照
従来のSQLの問題点
-- 開発環境
SELECT * FROM dev_database.dev_schema.users;
-- 本番環境(手動で書き換えが必要)
SELECT * FROM prod_database.prod_schema.users;
dbtのref関数
-- どの環境でも同じコード
SELECT * FROM {{ ref('users') }};
-- dbt_project.ymlで環境ごとの設定を管理
# 開発環境: dev_database.dev_schema.users
# 本番環境: prod_database.prod_schema.users
メリット3:リネームや移動に強い
従来のSQLの問題点
-- usersテーブルを参照している箇所が10箇所ある
SELECT * FROM users; -- 箇所1
SELECT * FROM users; -- 箇所2
...
-- usersをstg_usersにリネームした場合
-- 10箇所すべてを手動で修正する必要がある
dbtのref関数
-- すべての箇所でref関数を使用
SELECT * FROM {{ ref('users') }};
-- ファイル名を変更するだけで、参照は自動で更新される
# models/users.sql → models/stg_users.sql
3.3 ref機能の注意点
注意点1:循環参照は不可
-- models/table_a.sql
SELECT * FROM {{ ref('table_b') }}
-- models/table_b.sql
SELECT * FROM {{ ref('table_a') }}
-- エラー:循環参照
-- Error: Cycle detected: table_a -> table_b -> table_a
注意点2:ファイル名とモデル名の一致
ファイル名: stg_users.sql
ref関数: {{ ref('stg_users') }}
# ファイル名をそのまま使用(拡張子なし)
注意点3:source関数との使い分け
| 関数 | 用途 | 例 |
|---|---|---|
| source() | 生データ(外部テーブル)を参照 | {{ source('raw_data', 'users') }} |
| ref() | dbtモデル(自分で作成したテーブル)を参照 | {{ ref('stg_users') }} |
4. source機能:「生データをどう参照するの?」
4.1 source機能とは
source機能は、データウェアハウスに既に存在する 生データ(外部テーブル) を参照するための機能です。
【データの流れ】
生データ(外部から投入)
↓ source()で参照
ステージング(dbtで作成)
↓ ref()で参照
マート(dbtで作成)
4.2 最初の疑問:「テーブル名を直接書けば良くない?」
疑問
-- 普通にテーブル名を書けば良いのでは?
SELECT * FROM raw_database.raw_schema.users;
しかし、source関数を使うことで 多くのメリット が得られます。
4.3 source機能のメリット
メリット1:データソースの一元管理
従来の方法の問題点
-- models/stg_users.sql
SELECT * FROM raw_database.public.users;
-- models/stg_orders.sql
SELECT * FROM raw_database.public.orders;
-- models/stg_products.sql
SELECT * FROM raw_database.public.products;
-- データベース名を変更したい場合
-- → すべてのファイルを手動で修正する必要がある
sourceを使った場合
# models/sources.yml(一箇所で管理)
version: 2
sources:
- name: raw_data
database: raw_database
schema: public
tables:
- name: users
- name: orders
- name: products
-- models/stg_users.sql
SELECT * FROM {{ source('raw_data', 'users') }}
-- models/stg_orders.sql
SELECT * FROM {{ source('raw_data', 'orders') }}
-- models/stg_products.sql
SELECT * FROM {{ source('raw_data', 'products') }}
-- データベース名の変更は sources.yml の1箇所を修正するだけ
メリット2:データ鮮度チェック
sourceにはfreshnessチェック機能があります
# models/sources.yml
version: 2
sources:
- name: raw_data
database: raw_database
schema: public
tables:
- name: users
loaded_at_field: updated_at # 更新日時のカラム
freshness:
warn_after: {count: 12, period: hour} # 12時間でwarning
error_after: {count: 24, period: hour} # 24時間でerror
# データの鮮度をチェック
dbt source freshness
# 結果例
# WARN: Source raw_data.users is 13 hours old
メリット3:ドキュメント生成
# models/sources.yml
version: 2
sources:
- name: raw_data
description: "EC サイトの生データ"
database: raw_database
schema: public
tables:
- name: users
description: "ユーザーマスタテーブル"
columns:
- name: user_id
description: "ユーザーID(主キー)"
- name: email
description: "メールアドレス"
- name: orders
description: "注文トランザクションテーブル"
# ドキュメントを生成
dbt docs generate
dbt docs serve
# → ブラウザで生データの定義を確認できる
4.4 source機能の基本的な使い方
ステップ1:sources.ymlの作成
models/sources.yml
version: 2
sources:
- name: raw_data # sourceの名前(任意)
database: raw_database # データベース名
schema: public # スキーマ名
tables:
- name: users # テーブル名
- name: orders
- name: products
ステップ2:モデルでsource関数を使用
models/staging/stg_users.sql
SELECT
user_id,
LOWER(email) AS email,
created_at
FROM {{ source('raw_data', 'users') }}
WHERE deleted_at IS NULL
基本構文
{{ source('source名', 'テーブル名') }}
ステップ3:実行と確認
# モデルを実行
dbt run
# 生成されるSQL
# SELECT ... FROM raw_database.public.users WHERE deleted_at IS NULL
4.5 ref関数とsource関数の使い分け
使い分けの基本ルール
【データの流れ】
外部データ投入
↓
生テーブル(raw_users)
↓ ← source('raw_data', 'users')
stg_users(dbtモデル)
↓ ← ref('stg_users')
user_summary(dbtモデル)
↓ ← ref('user_summary')
最終テーブル(dbtモデル)
明確な基準
| データの種類 | 使う関数 | 例 |
|---|---|---|
| dbt外部からのデータ | source() |
ETLツールで投入したデータ |
| dbtで作成したテーブル | ref() |
dbtモデルで生成したテーブル |
| seedで作成したテーブル | ref() |
CSVから作成したマスタ |
4.6 source機能の注意点
注意点1:テーブルが存在しない場合
# エラー例
Error: Database Error in source raw_data.users
relation "raw_database.public.users" does not exist
対処法
- sources.ymlの設定を確認
- データベースに実際にテーブルが存在するか確認
- スキーマ名、テーブル名のスペルミスを確認
注意点2:複数のデータベースを扱う場合
# models/sources.yml
version: 2
sources:
# EC サイトのデータ
- name: ecommerce_data
database: ecommerce_db
schema: public
tables:
- name: users
- name: orders
# CRM システムのデータ
- name: crm_data
database: crm_db
schema: public
tables:
- name: customers
- name: contacts
-- 異なるsourceからのデータを結合
SELECT
e.user_id,
e.email,
c.customer_type
FROM {{ source('ecommerce_data', 'users') }} AS e
LEFT JOIN {{ source('crm_data', 'customers') }} AS c
ON e.email = c.email
4.7 実践的なsources.ymlの書き方
完全な設定例
version: 2
sources:
- name: raw_data
description: "EC サイトの生データ(毎日ETLで更新)"
database: raw_database
schema: public
tables:
- name: raw_users
description: "ユーザーマスタの生データ"
loaded_at_field: updated_at
freshness:
warn_after: {count: 12, period: hour}
error_after: {count: 24, period: hour}
columns:
- name: user_id
description: "ユーザーID(主キー)"
tests:
- unique
- not_null
- name: email
description: "メールアドレス"
tests:
- not_null
- name: created_at
description: "作成日時"
- name: updated_at
description: "更新日時"
- name: deleted_at
description: "削除日時(論理削除)"
- name: raw_orders
description: "注文トランザクションの生データ"
loaded_at_field: created_at
freshness:
warn_after: {count: 6, period: hour}
error_after: {count: 12, period: hour}
columns:
- name: order_id
description: "注文ID(主キー)"
tests:
- unique
- not_null
- name: user_id
description: "ユーザーID(外部キー)"
tests:
- not_null
- name: amount
description: "注文金額"
- name: status
description: "注文ステータス"
5. まとめ
5.1 seed機能のポイント
使うべき場面
- マスタデータの管理(国コード、カテゴリなど)
- 環境間でのデータ同期
- チームでのデータ共有
メリット
- バージョン管理が容易(Git管理)
- 更新が簡単(CSVを編集するだけ)
- 環境間の同期が確実
注意点
- 大量データには不向き(数千行程度まで)
- 文字コードはUTF-8
5.2 ref機能のポイント
使うべき理由
- 依存関係の自動管理
- 環境に依存しない参照
- メンテナンス性の向上
重要な特徴
- 実行順序を自動解決
- テーブル名の変更に強い
- 依存関係の可視化が可能
基本的な使い方
- ファイル名(拡張子なし)を引数に指定
- dbtで作成したテーブル・seedを参照
- JOINでも通常のテーブルと同様に使用
5.3 source機能のポイント
使うべき理由
- 外部データソースの一元管理
- データ鮮度チェック機能
- ドキュメント自動生成
重要な特徴
- sources.ymlで定義を一元管理
- データベース名の変更に強い
- テスト・ドキュメント機能との統合
基本的な使い方
- sources.ymlで定義
- dbt外部から投入されたデータを参照
- ステージング層での使用が推奨
5.4 3つの機能の使い分けまとめ
| 機能 | データの種類 | 使用場面 | 例 |
|---|---|---|---|
| seed | 静的マスタデータ | 手動管理する小規模データ | 国コード、カテゴリ、地域マスタ |
| source | 外部投入データ | dbt外部から投入される生データ | ETLで取得したトランザクションデータ |
| ref | dbt作成データ | dbtで変換・集計したテーブル | ステージング、マート、集計テーブル |