dbtをとりあえず動かしたら混乱したので、何をしているツールなのか整理した
はじめに
dbt(Data Build Tool)を触ってみた。
-
dbt runが通った - view や table ができた
- docs も見れた
……が、正直よくわからなかった。
この記事は、
- dbtを「とりあえず動かした人」
- でも「何をしているツールなのか腹落ちしていない人」
向けに、自分が理解するために整理したメモです。
まずは dbt を「動かしてみた」
理解する前に、とにかくローカルで dbt を動かしてみた。
今回は Docker + Postgres を使い、Mac(Apple Silicon)上で検証している。
ディレクトリ構成
<root>
├─ docker-compose.yml
├─ dbt/
│ ├─ Dockerfile
│ └─ profiles.yml
docker-compose.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: dbt
POSTGRES_PASSWORD: dbt
POSTGRES_DB: analytics
ports:
- "5432:5432"
dbt:
build:
context: ./dbt
dockerfile: Dockerfile
platform: linux/amd64
volumes:
- ./dbt:/usr/app/dbt
working_dir: /usr/app/dbt/dbt_lab
environment:
DBT_PROFILES_DIR: /usr/app/dbt
depends_on:
- postgres
ports: ["8080:8080"]
Dockerfile
dbt/Dockerfile
FROM ghcr.io/dbt-labs/dbt-postgres:1.7.9
RUN pip install --no-cache-dir "protobuf<5"
profiles.yml
dbt/profiles.yml
dbt_lab:
target: dev
outputs:
dev:
type: postgres
host: postgres
user: dbt
password: dbt
port: 5432
dbname: analytics
schema: public
Postgres はデータ保存用、dbt は DBに対して操作を行うだけのCLI である。
生データ(raw)を用意する
postgresのコンテナに入って
psql -d analytics -U dbt
でDBに入る。
テーブルとサンプルデータを挿入する。
CREATE TABLE raw_users (
id SERIAL PRIMARY KEY,
name TEXT,
is_active BOOLEAN
);
INSERT INTO raw_users (name, is_active) VALUES
('Alice', true),
('Bob', false),
('Carol', true);
この raw_users は dbtが作るものではない。dbtは「すでに存在する生データ」を前提にする。
dbtの初期化
docker compose run --rm dbt init dbt_lab
いろいろ聞かれるが、上記のprofiles.ymlに記載の内容で入力すればよい。
これで各種ファイルが生成される。
staging(下ごしらえ)モデルを書く
models/staging/stg_users.sql
SELECT
id,
name,
is_active
FROM raw_users
WHERE is_active = true
ここでは不要なデータを落としただけ。集計や計算はしていない。
dbt run を実行する
docker compose run --rm dbt run
これにより dbt は
- SQLをDBに投げ
- stg_users という view を作成した
schema.ymlを追加
dbt/dbt_lab/models/staging/schema.yml
version: 2
models:
- name: stg_users
description: "raw_users から有効ユーザーだけ抽出したステージングビュー"
columns:
- name: id
description: "ユーザーID"
tests:
- not_null
- unique
- name: name
description: "ユーザー名"
集計モデルを書く
dbt/dbt_lab/models/analytics/active_user_count.sql
{{ config(materialized='table') }}
SELECT
COUNT(*) AS active_user_count
FROM {{ ref('stg_users') }}
- ref() で依存関係を明示
- 結果は table として保存
dbt test / docs
docker compose run --rm dbt test
docker compose run --rm dbt docs generate
docker compose run --rm --service-ports dbt docs serve
testはschema.ymlに書いた内容と実際のデータが相違ないかをテストする。
docs を開くと、モデルの依存関係が自動で可視化された。
この時点で、
- view ができた
- table ができた
- docs も見れた
……が、動かして自分の言葉で説明できないと正直よくわからなかった。
- dbtが何をしているのか
- なぜ view と table を分けるのか
- どこにデータが残っているのか
が曖昧なままだった。
そこで、「dbtは何者なのか」を整理することにした。
dbtは何をするツールか(結論)
dbtはデータベースでも、APIでもない。
- SQLで変換処理を書き
- 依存関係を明示し
- view / table を適切に作り分け
- DBに「使いやすい形」を用意する
ための ビルドツール だった。
- dbt自体はデータを持たない
- クエリを受け付けるサーバでもない
- 実際にデータを持つのは DB(BigQuery / Postgres など)
という点が最重要です。
dbt run は何をしている?
dbt run は、
-
.sqlファイルを読む - そのSQLを DBに投げる
- 結果を view / table としてDBに作る
だけです。
つまり、dbtは「SQLを実行してDBオブジェクトを作る係」
stg(staging)とは何か?
stg_usersのようなモデルは staging(下ごしらえ) を意味します。
stgの特徴
- rawデータを少し整えるだけ
- 行数はほぼ元データと同じ
- 集計はしない
- 再利用される部品
SELECT *
FROM raw_users
WHERE is_active = true
料理で言うと、材料を洗って切った状態
集計(analytics / mart)とは何か?
一方、active_user_countのようなモデルは 集計結果 です。
集計の特徴
- 行数が激減(1行など)
- BIやアプリが直接使う
- よくクエリされる
SELECT COUNT(*) FROM stg_users
料理で言うと、完成品
なぜ「stgはview、集計はtable」なのか?
BigQueryではクエリコスト=スキャンしたデータ量です。
stgをtableにすると
- rawの巨大データを保存
- でも別の集計でまた raw を読む
→ コスト削減にならない
集計をviewにすると
BIが叩くたびに raw をフルスキャン
→ クエリコスト爆増
よくある正解パターン
raw(巨大)
↓
stg(view)
↓
集計(table)
- stg:保存しない
- 集計:保存して再利用
ref() は何が嬉しい?
FROM {{ ref('stg_users') }}
これは、**「このモデルは stg_users に依存している」**という 宣言 です。
ref を使うことで:
- 実行順序をdbtが決める
- 名前変更に強くなる
- docsで依存グラフが描ける
SQLの入れ子を「ファイル分割」しただけ、と考えると理解しやすい。
materialized='table' は何をした?
{{ config(materialized='table') }}
これは dbt に対して、**「このSQLの結果を 物理テーブルとして保存して」**と伝えています。
- view:毎回計算
- table:一度計算して保存
BigQueryでは、集計はtableにすることでクエリコストを下げる のが重要です。
毎日データが増えたらテーブルも増える?
増えません。毎日dbt runしても同じテーブルが更新されるだけ、日付付きテーブルが勝手に増えることはない。
ただし、毎日フル再計算するとコストが高いので
→ incremental model や partition を使う
これは別途ちゃんと考える必要があります。
アプリケーションはどう使う?
重要な点としてアプリやBIは dbt を一切意識しない
App / BI
↓
DB(dbtで作られた table / view)
dbtは 事前に整備する係 です。
まとめ
dbtは魔法のツールではなく、
- SQLを整理し
- 依存関係を明示し
- view / table を適切に作り分け
- コストと可読性を守る
ための仕組みでした。
「とりあえず動かしたけどよくわからない」状態から、
「何をしているか説明できる」状態になるまでの整理として、
同じように迷っている人の参考になれば幸いです。
Discussion