概要
ETLのデータ変換(ETLのTransfer)を効率的に行いたい際にdbtを使うケースが増えています
今回はdbtをはじめて触る人向けに
- dbtの概要
- ディレクトリ構成
- 操作方法
などについて解説していきます
前提
- 今回は検証しやすいようにPostgresのコンテナを使って解説します
- パッケージ管理はuvを使用
dbtとは?
データ変換(ETLのTransfer)を SQLベースでシンプルに実装できるツールです
従来は、Pythonや専用のツールを使って記述していましたが、dbtを使えばSQLの知識があればデータの
- 変換
- 管理
- テスト
などを効率的に行うことができます
データの抽出(ETLのExtract)やロード(ETLのLoad)は出来ませんが
- Google CloudのBigQuery
- AWSのRedshift
などのDWH(データウェアハウス)に読み込まれた生データを分析しやすい形へ変換することに優れています
コンテナ環境構築
Postgresを使用するためのコンテナ環境を構築します
以下のディレクトリ構成で進めていきます
必要なファイルおよびフォルダは後ほど説明していきます
tree
.
├── .env
├── .gitignore
├── Makefile
├── README.md
├── application
│ ├── .python-version
│ ├── README.md
│ ├── pyproject.toml
│ └── uv.lock
├── containers
│ └── postgres
│ ├── Dockerfile
│ └── init.sql
└── docker-compose.yml
今回はPostgresを使用するのでdbt-coreとdbt-postgresをインストールします
uv add dbt-core
uv add dbt-postgres
- Dockerfile
- docker-compose.yml
- .env
にPostgresの設定を記載していきます
FROM postgres:17.3
services:
db:
container_name: db
build:
context: .
dockerfile: containers/postgres/Dockerfile
volumes:
- db_data:/var/lib/postgresql/data
- ./containers/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
environment:
- POSTGRES_NAME
- POSTGRES_USER
- POSTGRES_PASSWORD
ports:
- "5432:5432" # デバッグ用
volumes:
db_data:
環境変数を記載します
今回は
- DB名
- ユーザ名
- パスワード
をすべてpostgresにします
POSTGRES_NAME=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
init.sqlをマウントしてテーブルとデータを作成するSQLを記載します
-- スキーマ作成
-- Postgresではschemaがデフォルトでpublicになるため、dbt用のschemaを作成する
CREATE SCHEMA dbt_dev;
-- Users テーブルの作成
CREATE TABLE "postgres"."dbt_dev"."users" (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(255) UNIQUE NOT NULL,
first_name VARCHAR(255),
last_name VARCHAR(255),
password VARCHAR(255) NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
role VARCHAR(50),
phone_number VARCHAR(20)
);
-- Todos テーブルの作成
CREATE TABLE "postgres"."dbt_dev"."todos" (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
priority INT,
complete BOOLEAN DEFAULT FALSE
);
-- Users テーブルにテストデータを挿入
INSERT INTO "postgres"."dbt_dev"."users" (email, username, first_name, last_name, password, is_active, role, phone_number)
VALUES
('user1@example.com', 'user1', 'John', 'Doe', 'hashedpassword1', TRUE, 'admin', '123-456-7890'),
('user2@example.com', 'user2', 'Jane', 'Smith', 'hashedpassword2', TRUE, 'user', '234-567-8901'),
('user3@example.com', 'user3', 'Alice', 'Johnson', 'hashedpassword3', FALSE, 'user', '345-678-9012');
-- Todos テーブルにテストデータを挿入
INSERT INTO "postgres"."dbt_dev"."todos" (title, description, priority, complete)
VALUES
('Buy groceries', 'Buy milk, eggs, and bread', 2, FALSE),
('Finish project', 'Complete the database migration', 1, FALSE),
('Call mom', 'Check in with mom and chat', 3, TRUE);
以上のファイル群を作成した後はコンテナのbuild、upを実行します
その後、コンテナ内に入り、Postgresの起動、DB、テーブル、データの作成を確認できれば成功です
docker-compose up -d --build
docker exec -it db bash
root@a1c3e2cf55fb:/# psql -U postgres
psql (17.3 (Debian 17.3-1.pgdg120+1))
Type "help" for help.
postgres=# \c postgres
You are now connected to database "postgres" as user "postgres".
postgres=# \dt dbt_dev.*
List of relations
Schema | Name | Type | Owner
---------+-------+-------+----------
dbt_dev | todos | table | postgres
dbt_dev | users | table | postgres
(2 rows)
postgres=# select * from "dbt_dev"."todos";
id | title | description | priority | complete
----+----------------+---------------------------------+----------+----------
1 | Buy groceries | Buy milk, eggs, and bread | 2 | f
2 | Finish project | Complete the database migration | 1 | f
3 | Call mom | Check in with mom and chat | 3 | t
(3 rows)
postgres=# select * from "dbt_dev"."users";
id | email | username | first_name | last_name | password | is_active | role | phone_number
----+-------------------+----------+------------+-----------+-----------------+-----------+-------+--------------
1 | user1@example.com | user1 | John | Doe | hashedpassword1 | t | admin | 123-456-7890
2 | user2@example.com | user2 | Jane | Smith | hashedpassword2 | t | user | 234-567-8901
3 | user3@example.com | user3 | Alice | Johnson | hashedpassword3 | f | user | 345-678-9012
(3 rows)
dbtのディレクトリ構成の説明
dbtのプロジェクトにおいてデータ変換やテスト、ドキュメント管理のための一般的なフォルダ構成が存在します
以下のフォルダおよびファイルを作成しましょう
フォルダ名 | 説明 | 役割・ポイント |
---|---|---|
models/ | SQLモデルを定義するフォルダ |
dbt run で実行する SQL を配置 |
seeds/` | CSV ファイルをデータとして取り込む** |
dbt seed で DWH にロード |
tests/ | データ品質をテストする SQL を格納 |
dbt test で NULL チェックなど |
snapshots/ | データの履歴を管理する |
dbt snapshot で SCD(履歴管理)を実装 |
macros/ | 再利用可能な SQL テンプレート | Jinja で SQL を動的に生成 |
analysis/ | 分析用の SQL クエリを保存 |
dbt run には含まれない |
target/ | dbt 実行時の出力結果を保存 |
dbt run の成果物やログを格納 |
logs/ | 実行時のログ情報を記録 | デバッグ・エラー調査に使用 |
dbt_project.yml | プロジェクト設定ファイル | モデルのスキーマ・パス設定など |
packages.yml | dbtの外部ライプラリ情報 | dbtの外部ライプラリを管理 (~/.dbt/ に配置) |
package-lock.yml | dbtの外部ライプラリ情報 | dbtの外部ライプラリの依存関係を管理 (~/.dbt/ に配置) |
profiles.yml | データベース接続情報 | DWH の認証情報を設定 (~/.dbt/ に配置) |
tree
├── .env
├── .gitignore
├── Makefile
├── README.md
├── application
│ ├── .python-version
│ ├── .venv
│ ├── README.md
│ ├── analysis
│ ├── dbt_project.yml
│ ├── logs
│ ├── macros
│ ├── main.py
│ ├── models
│ ├── profiles.yml
│ ├── pyproject.toml
│ ├── seeds
│ ├── snapshots
│ ├── target
│ ├── tests
│ └── uv.lock
├── containers
│ └── postgres
│ ├── Dockerfile
│ └── init.sql
└── docker-compose.yml
上記のディレクトリ構成内で初期設定に必要なファイルは以下の通りです
- dbt_project.yml
- profiles.yml
順番に解説していきます
dbt_project.yml
dbtのプロジェクトに必須のファイルでプロジェクトの設定を記載します
# https://docs.getdbt.com/reference/dbt_project.yml
# プロジェクト名
name: 'dbt_practice'
config-version: 2
version: '1.0.0'
# プロファイル、DWの接続設定名
profile: 'dbt_practice'
# 各フォルダのパス
model-paths: ["models"]
analysis-paths: ["analysis"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]
# dbt コマンドの出力先パス
target-path: "target"
# dbt clean コマンドを実行したときに削除対象のディレクトリ
clean-targets: [target, dbt_packages]
# どのmodelをどのスキーマと紐付けるかの設定
models:
dbt_practice:
schema: dbt_dev
profiles.yml
DWHとの接続情報を定義するファイルです
今回はPostgresを使用しているので
- ホスト
- ユーザ名
- パスワード
- ポート
- DB名
- スキーマ
などの設定を記載します
dbt_practice:
target: dev
outputs:
dev:
type: postgres
host: localhost
user: postgres
password: postgres
port: 5432
dbname: postgres
schema: dbt_dev
threads: 1
keepalives_idle: 0
connect_timeout: 10
packages.yml
- dbt_utils
- elementary
などdbt-coreのデフォルトの機能だけで対応できない実装などをする際に外部ライブラリを利用することが多いです
packages.ymlにdbt用のライブラリ情報を記載していきます
# This is your packages.yml file. This is where you declare packages that your project depends on.
# For full documentation: https://docs.getdbt.com/docs/building-a-dbt-project/package-management
packages:
# The dbt_utils package is a package we recommend everyone installs. This package solves common questions of `How do I do this in SQL?`.
# To learn more, check out the docs: https://hub.getdbt.com/dbt-labs/dbt_utils/latest/
- package: dbt-labs/dbt_utils
version: 1.3.0
## Docs: https://docs.elementary-data.com
- package: elementary-data/elementary
version: 0.16.1
dbtで使用するライブラリはdbt depsコマンドを使ってインストールします
dbt deps
02:14:57 Running with dbt=1.9.2
02:14:58 Updating lock file in file path: /Users/shun/dbt-practice/application/package-lock.yml
02:14:58 Installing dbt-labs/dbt_utils
02:14:58 Installed from version 1.3.0
02:14:58 Up to date!
02:14:58 Installing elementary-data/elementary
02:14:59 Installed from version 0.16.1
02:14:59 Updated version available: 0.16.4
02:14:59
02:14:59 Updates available for packages: ['elementary-data/elementary']
Update your versions in packages.yml, then run dbt deps
package-lock.yml
packages.ymlのlockファイルでdbt depsコマンドを実行すれば自動生成されます
今回インストールした
- dbt_utils
- elementary
の依存関係を管理します
packages:
- package: dbt-labs/dbt_utils
version: 1.3.0
- package: elementary-data/elementary
version: 0.16.1
sha1_hash: 5e638e51b8a19f6d626e17b8fbaeb0b4c026abd1
dbt_utilsやelementaryの使い方などは今後別記事で解説したいと思います
modelの作成
dbtにおけるmodelはデータを変換・加工するためのSQLクエリを指します
ディレクトリ構成で記載したmodels/フォルダ内に.sqlファイルを作成し、そこにSELECTクエリを書くことで、データの成形・整理を行います
今回はusersテーブルのfirst_nameとlast_nameを結合し、full_nameカラムを作成します
select
"id",
concat("first_name", " ", "last_name") as full_name
from
"postgres"."dbt_dev"."users"
dbt runコマンドを実行するとuser_name という新しいdbtモデル(view)が定義され、クエリの結果が DWH(今回だとPostgres)内のviewに保存されます
以下のようにviewが作成され、user_nameのviewにlast_nameのカラム、userテーブル内のfirst_nameとlast_nameが結合されたデータが保存されたら成功です
dbt run
01:45:28 Running with dbt=1.9.2
01:45:28 Registered adapter: postgres=1.9.0
01:45:28 [WARNING]: Configuration paths exist in your dbt_project.yml file which do not apply to any resources.
There are 1 unused configuration paths:
- models.application
01:45:28 Found 1 model, 433 macros
01:45:28
01:45:28 Concurrency: 1 threads (target='dev')
01:45:28
01:45:29 1 of 1 START sql view model dbt_dev.user_name .................................. [RUN]
01:45:29 1 of 1 OK created sql view model dbt_dev.user_name ............................. [CREATE VIEW in 0.10s]
01:45:29
01:45:29 Finished running 1 view model in 0 hours 0 minutes and 0.28 seconds (0.28s).
01:45:29
01:45:29 Completed successfully
01:45:29
01:45:29 Done. PASS=1 WARN=0 ERROR=0 SKIP=0 TOTAL=1
postgres=# select * from dbt_dev.user_name;
id | full_name
----+---------------
1 | John Doe
2 | Jane Smith
3 | Alice Johnson
(3 rows)
view の一覧は以下のコマンドを実行すれば確認できます
postgres=# \dv dbt_dev_dbt_dev.*
List of relations
Schema | Name | Type | Owner
-----------------+-----------+------+----------
dbt_dev_dbt_dev | user_name | view | postgres
(1 row)
テストの実行
dbtでは作成したviewの単体テストを作成できます
単体テストを作成することで該当カラムにNULLが入らないことや外部キーが正しく紐付いているか、などを担保することができます
今回はuser_nameのviewのfull_nameカラムがnullにならないテストを作成します
-- full_nameがNULLの時、idを返すテスト
select
id
from
{{ ref('user_name') }}
where
full_name is null
テストを実行するときはdbt testコマンドを実行します
以下のようにテストが実行され、通ることが確認できれば成功です
dbt test
03:03:51 Running with dbt=1.9.2
03:03:51 Registered adapter: postgres=1.9.0
03:03:52 Found 30 models, 2 seeds, 2 operations, 1 test, 1235 macros
03:03:52
03:03:52 Concurrency: 1 threads (target='dev')
03:03:52
03:03:52
IMPORTANT - Starting from dbt 1.8, users must explicitly allow packages to override materializations.
Elementary requires this ability to support collection of samples and failed row count for dbt tests.
Please add the following flag to dbt_project.yml to allow it:
flags:
require_explicit_package_overrides_for_builtin_materializations: false
Notes -
* This is a temporary measure that will result in a deprecation warning, please ignore it for now. Elementary is working with the dbt-core team on a more permanent solution.
* This message can be muted by setting the 'mute_ensure_materialization_override' var to true.
03:03:52 1 of 1 START hook: elementary.on-run-start.0 ................................... [RUN]
03:03:52 1 of 1 OK hook: elementary.on-run-start.0 ...................................... [OK in 0.10s]
03:03:52
03:03:52 1 of 1 START test user_name_is_not_null ........................................ [RUN]
03:03:52 1 of 1 PASS user_name_is_not_null .............................................. [PASS in 0.07s]
03:03:52
03:03:54 1 of 1 START hook: elementary.on-run-end.0 ..................................... [RUN]
03:03:54 1 of 1 OK hook: elementary.on-run-end.0 ........................................ [OK in 1.65s]
03:03:54
03:03:54 Finished running 2 project hooks, 1 test in 0 hours 0 minutes and 2.08 seconds (2.08s).
03:03:54
03:03:54 Completed successfully
03:03:54
03:03:54 Done. PASS=3 WARN=0 ERROR=0 SKIP=0 TOTAL=3
dbt ドキュメントの生成と閲覧
- dbt docs generate
- dbt docs serve
コマンドを使用することでdbtドキュメントの生成およびローカルホスト上でdbtドキュメントを閲覧することができるようになります
dbt docs generate
02:23:11 Running with dbt=1.9.2
02:23:12 Registered adapter: postgres=1.9.0
02:23:12 [WARNING]: Configuration paths exist in your dbt_project.yml file which do not apply to any resources.
There are 1 unused configuration paths:
- models.application
02:23:12 Found 30 models, 2 operations, 1235 macros
02:23:12
02:23:12 Concurrency: 1 threads (target='dev')
02:23:12
02:23:13
IMPORTANT - Starting from dbt 1.8, users must explicitly allow packages to override materializations.
Elementary requires this ability to support collection of samples and failed row count for dbt tests.
Please add the following flag to dbt_project.yml to allow it:
flags:
require_explicit_package_overrides_for_builtin_materializations: false
Notes -
* This is a temporary measure that will result in a deprecation warning, please ignore it for now. Elementary is working with the dbt-core team on a more permanent solution.
* This message can be muted by setting the 'mute_ensure_materialization_override' var to true.
02:23:14 Building catalog
02:23:14 Catalog written to /Users/shun/dbt-practice/application/target/catalog.json
dbt docs serve
02:23:32 Running with dbt=1.9.2
Serving docs at 8080
To access from your browser, navigate to: http://localhost:8080
Press Ctrl+C to exit.
127.0.0.1 - - [16/Feb/2025 11:23:33] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [16/Feb/2025 11:23:33] "GET /manifest.json?cb=1739672613865 HTTP/1.1" 200 -
127.0.0.1 - - [16/Feb/2025 11:23:33] "GET /catalog.json?cb=1739672613865 HTTP/1.1" 200 -
127.0.0.1 - - [16/Feb/2025 11:23:35] code 404, message File not found
127.0.0.1 - - [16/Feb/2025 11:23:35] "GET /$%7Brequire('./assets/favicons/favicon.ico')%7D HTTP/1.1" 404 -
http://localhost:8080
へアクセスすると閲覧できます
DBとの接続テストおよびデバッグ用の情報の表示したい時
dbt debugコマンドを使用することで
- DBとの接続テスト
- デバッグ用の情報の表示
を行うことができます
dbt debug
03:12:50 Running with dbt=1.9.2
03:12:50 dbt version: 1.9.2
03:12:50 python version: 3.11.11
03:12:50 python path: /Users/shun/dbt-practice/application/.venv/bin/python3
03:12:50 os info: macOS-14.6.1-x86_64-i386-64bit
03:12:50 Using profiles dir at /Users/shun/dbt-practice/application
03:12:50 Using profiles.yml file at /Users/shun/dbt-practice/application/profiles.yml
03:12:50 Using dbt_project.yml file at /Users/shun/dbt-practice/application/dbt_project.yml
03:12:50 adapter type: postgres
03:12:50 adapter version: 1.9.0
03:12:51 Configuration:
03:12:51 profiles.yml file [OK found and valid]
03:12:51 dbt_project.yml file [OK found and valid]
03:12:51 Required dependencies:
03:12:51 - git [OK found]
03:12:51 Connection:
03:12:51 host: localhost
03:12:51 port: 5432
03:12:51 user: postgres
03:12:51 database: postgres
03:12:51 schema: dbt_dev
03:12:51 connect_timeout: 10
03:12:51 role: None
03:12:51 search_path: None
03:12:51 keepalives_idle: 0
03:12:51 sslmode: None
03:12:51 sslcert: None
03:12:51 sslkey: None
03:12:51 sslrootcert: None
03:12:51 application_name: dbt
03:12:51 retries: 1
03:12:51 Registered adapter: postgres=1.9.0
03:12:51 Connection test: [OK connection ok]
03:12:51 All checks passed!
まとめ
dbtの基礎的な知識についてまとめてみましたがいかがでしたでしょうか?
他に汎用的で便利な使用用途があれば随時更新していきたいと思います
参考