dbt(data build tool)は、SQLを使ったデータ変換(ELTのT部分)を効率化し、データ分析基盤の開発を「ソフトウェアエンジニアリングのように」洗練させるツールです。 複雑なSQLの依存関係を自動で管理し、テストやドキュメント生成までをコマンド一つで自動化できるため、現代のモダンデータスタックにおいて事実上の標準として広く親しまれています。
dbtは本来Snowflake・BigQuery(GCP)・Redshift(AWS)などのクラウド技術を前提としたデータウェアハウスに接続して使うツールです。しかし、上記クラウドサービスは有償・クレジットカード情報の登録が前提となっており、初心者・初学者にとって、まずは気軽に触ってみる・動かしてみる ことのハードルが若干高くもあります。
そこで、このノートブックでは、
- 環境構築が不要で、いちばん手軽に
- 完全に無料のサービスのみで
dbtの操作を、触りながら確認する手法を考えてみたいと思います。
1を達成するためにGoogle Colab、2を達成するためにSQLiteを使用します。SQLiteはファイル1つで動くデータベースなので、認証情報もクラウド契約も不要で、Colab上のローカルファイルとしてそのまま使用しています。
このノートブックで体験すること
- Colab上にdbtとSQLiteアダプタ(
dbt-sqlite)をインストールする - 少し「汚れた」顧客マスタと注文トランザクションの2つのテーブルを用意する
- dbtの最小構成プロジェクト(
dbt_project.yml/profiles.yml/ モデルファイル)を作る - それぞれのテーブルをdbtで軽くクレンジングし、
{{ ref(...) }}で結合して実用的な集計ビューを作る -
dbt runでSQLを組み立てて実行し、dbt testでデータ品質チェックをする - 最後に、dbtが実際に発行したSQLをのぞいて確認する
「マスタとトランザクションを結合して分析用のテーブルを作る」は、実務のデータ変換で最も頻出するパターンの1つです。dbt Cloudのアカウント登録も、外部データウェアハウスの契約も不要で、データ分析の仕組みづくりの手触り を知ることができます。
すぐに使えるチートシートはこちら Google Colab版
すぐに実行して、試せるコードレシピはこちら。
0. 前提知識ひとことメモ
-
dbt: SQLの
SELECT文を書くと、それをテーブルやビューとして実体化(materialize)してくれるツール。生データを分析用の形に変換する「T (Transform)」の部分を担当する。 -
model: dbtにおける
.sqlファイル1つ1つのこと。1つのmodelは1つのSELECT文に対応し、実行すると1つのテーブル(またはビュー)になる。 -
ref(): モデル同士の依存関係を表すための関数。あるモデルの中で
{{ ref('別のモデル名') }}と書くと、dbtが「このモデルはあのモデルより後に実行する必要がある」と自動的に判断してくれる。実行順序を自分で管理しなくてよいのがdbtの大きな利点の一つ。 -
test:
not_null(NULLを許さない)やunique(重複を許さない)など、データの品質を検証する仕組み。YAMLに書くだけで使える。
(dbtにはまだ他にもsourceという、dbtの外で作られた生データを指す仕組みがありますが、このノートブックでは扱いをシンプルにするため使いません。生データのテーブルは、素のSQLで直接参照します。)
1. dbtとSQLiteアダプタをインストールする
dbt-core本体と、SQLite用のアダプタであるdbt-sqliteをインストールします。dbtはアダプタ(接続先ごとのプラグイン)方式になっていて、SnowflakeならDBT-snowflake、BigQueryならdbt-bigqueryのように接続先ごとに別パッケージをインストールする仕組みです。
バージョンはズレるとうまく動かないことがあるため、dbt-coreとdbt-sqliteのバージョンを合わせてインストールします(dbt-sqliteはdbt-coreと同じメジャー.マイナーバージョンに追随する運用になっています)。
!pip install -q "dbt-core==1.10.*" "dbt-sqlite==1.10.0"
実行結果の末尾に、次のようなprotobufのバージョン競合に関する警告が表示されることがあります。
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
grpcio-status 1.71.2 requires protobuf<6.0dev,>=5.26.1, but you have protobuf 6.33.6 which is incompatible.
google-ai-generativelanguage 0.6.15 requires protobuf!=4.21.0,...,<6.0.0dev,>=3.20.2, but you have protobuf 6.33.6 which is incompatible.
これはインストール失敗ではありません。このノートブックで行う入門レベルの検証とは無関係のエラーのため、無視して次に進んで問題ありません。
# インストールできたか、バージョンとアダプタの認識を確認する
!dbt --version
実行結果に Plugins: - sqlite: 1.10.0 のように表示されていれば、SQLiteアダプタが正しく認識されています。
もし dbt コマンドが見つからない、と出た場合は、Colabのメニューから「ランタイム」→「セッションを再起動」を実行してから、このセルより上から再実行してみてください。
2. サンプルデータを用意する: 顧客マスタ × 注文トランザクション
dbtは「すでにどこかにある生データ」を変換するツールなので、まずは変換対象になる生データを自分で作ります。ここでは実務でよくある構成として、2つのテーブルを用意します。
-
raw_customers(顧客マスタ): 顧客の基本情報。ただし、名前の前後に余計な空白が入っていたり、地域(region)が未入力(NULL)だったり、同じ顧客が誤って2回登録されていたりと、少し「汚れた」状態にしておきます。 -
raw_orders(注文トランザクション): 注文の明細。ただし、どの顧客か紐付かない注文(customer_idがNULL)や、金額がマイナスになっている不正な注文が混ざっています。
このあと、この2つをそれぞれクレンジングしてから結合し、「顧客ごとの注文サマリ」という実用的なビューを作ります。
import sqlite3
import os
os.makedirs("/content/dbt_hello", exist_ok=True)
DB_PATH = "/content/dbt_hello/raw.db"
con = sqlite3.connect(DB_PATH)
cur = con.cursor()
# --- 顧客マスタ(少し汚れている) ---
cur.execute("DROP TABLE IF EXISTS raw_customers")
cur.execute('''
CREATE TABLE raw_customers (
customer_id INTEGER,
customer_name TEXT,
region TEXT,
signup_date TEXT
)
''')
customers = [
(1, " Aya Tanaka ", "kanto", "2026-01-10"),
(2, "Bo Lee", None, "2026-02-15"), # regionが未入力
(3, "cong wang ", "kansai", "2026-03-01"),
(3, "cong wang ", "kansai", "2026-03-01"), # 誤って重複登録
(4, " Dan Kim", "kanto", "2026-03-20"),
(5, "Emi Sato", None, "2026-04-05"), # regionが未入力・注文実績なし
]
cur.executemany("INSERT INTO raw_customers VALUES (?,?,?,?)", customers)
# --- 注文トランザクション(少し汚れている) ---
cur.execute("DROP TABLE IF EXISTS raw_orders")
cur.execute('''
CREATE TABLE raw_orders (
order_id INTEGER,
customer_id INTEGER,
product TEXT,
amount REAL,
order_date TEXT
)
''')
orders = [
(1, 1, "Notebook", 1200, "2026-07-01"),
(2, 2, "Pen", 150, "2026-07-01"),
(3, 1, "Pen", 150, "2026-07-02"),
(4, 4, "Notebook", 1200, "2026-07-03"),
(5, 2, "Eraser", 80, "2026-07-03"),
(6, None, "Notebook", 1200, "2026-07-04"), # customer_idが不明な注文
(7, 1, "Eraser", 80, "2026-07-05"),
(8, 3, "Pen", -150, "2026-07-05"), # 金額がマイナス(不正なデータ)
]
cur.executemany("INSERT INTO raw_orders VALUES (?,?,?,?,?)", orders)
con.commit()
con.close()
print(f"raw_customers / raw_orders を {DB_PATH} に作成しました")
実行結果(例):
raw_customers / raw_orders を /content/dbt_hello/raw.db に作成しました
# 生データの中身を確認してみる(汚れている箇所に注目)
import pandas as pd
con = sqlite3.connect(DB_PATH)
display(pd.read_sql("SELECT * FROM raw_customers", con))
display(pd.read_sql("SELECT * FROM raw_orders", con))
con.close()
実行結果(例):
customer_id customer_name region signup_date
0 1 Aya Tanaka kanto 2026-01-10
1 2 Bo Lee None 2026-02-15
2 3 cong wang kansai 2026-03-01
3 3 cong wang kansai 2026-03-01
4 4 Dan Kim kanto 2026-03-20
5 5 Emi Sato None 2026-04-05
order_id customer_id product amount order_date
0 1 1.0 Notebook 1200.0 2026-07-01
1 2 2.0 Pen 150.0 2026-07-01
2 3 1.0 Pen 150.0 2026-07-02
3 4 4.0 Notebook 1200.0 2026-07-03
4 5 2.0 Eraser 80.0 2026-07-03
5 6 NaN Notebook 1200.0 2026-07-04
6 7 1.0 Eraser 80.0 2026-07-05
7 8 3.0 Pen -150.0 2026-07-05
顧客マスタでは、customer_id=3の行が完全に重複していたり、regionが空だったりします。注文トランザクションでは、customer_idがNoneの注文(誰の注文か分からない)や、amountがマイナスの注文(返品を誤って記録したようなデータ)が混ざっています。こうした「そのままでは分析に使えない」状態のデータを、次のステップでdbtを使って整えていきます。
3. dbtプロジェクトを作る
通常dbtでは dbt init コマンドで対話形式にプロジェクトの雛形を作りますが、dbt init はプロジェクト名やアダプタの種類を1つずつ質問してくる対話型コマンドで、Colabのようにセルを順番に実行していく環境とは相性がよくありません。
そこで、このノートブックではdbt initを使わず、必要なファイルを直接書き出す方法でプロジェクトを作ります。dbtを動かすために最低限必要なファイルは、次の2つです。
-
dbt_project.yml: このフォルダが「dbtプロジェクトである」ことをdbtに認識させるための設定ファイル。これが無いと、dbt runなどのコマンドはすべて「dbtプロジェクトが見つからない」というエラーになります。 -
profiles.yml: 「どのデータベースに、どうやって接続するか」を定義する接続設定ファイル。これが無いと、dbtはモデルをどこに対して実行すればいいか分からず、SQLを1本も発行できません。
つまりこの2つは、モデルの中身以前にdbtを動かすための必須条件です。逆に言えば、この2つさえあれば、モデル(.sqlファイル)が1つも無くてもdbt debugのようなコマンドは実行できます。
プロジェクトのディレクトリ構成は、次のようにシンプルにします。
dbt_hello_project/
├── dbt_project.yml ... プロジェクト全体の設定(必須)
└── models/
├── staging/ ... 生データを軽くクレンジングするモデル
│ ├── stg_customers.sql
│ ├── stg_orders.sql
│ └── schema.yml
└── marts/ ... マスタとトランザクションを結合するモデル
├── customer_order_summary.sql
└── schema.yml
import os
PROJECT_DIR = "/content/dbt_hello/dbt_hello_project"
os.makedirs(f"{PROJECT_DIR}/models/staging", exist_ok=True)
os.makedirs(f"{PROJECT_DIR}/models/marts", exist_ok=True)
os.makedirs("/content/dbt_hello/dbt_profiles", exist_ok=True)
print("ディレクトリを作成しました:", PROJECT_DIR)
実行結果(例):
ディレクトリを作成しました: /content/dbt_hello/dbt_hello_project
name: 'dbt_hello_project'
version: '1.0.0'
config-version: 2
# profiles.yml側で定義するプロファイル名と一致させる
profile: 'dbt_hello_profile'
model-paths: ["models"]
clean-targets:
- "target"
- "dbt_packages"
models:
dbt_hello_project:
staging:
+materialized: view
marts:
+materialized: table
実行結果(例):
Writing /content/dbt_hello/dbt_hello_project/dbt_project.yml
dbt_project.ymlはプロジェクト全体の設定ファイルです。ここでは、stagingフォルダのモデルは軽量なview(ビュー)として、martsフォルダのモデルはtable(実体テーブル)として作る、というルールを指定しています。
4. 接続設定(profiles.yml)を書く
dbtは「どのプロジェクトを」「どのデータベースに」つなぐかを、profiles.ymlという別ファイルで管理します。プロジェクトのコードと接続情報(パスワードなど)を分離するための仕組みです。前述の通り、これが無いとdbtは接続先が分からず何も実行できないため、dbt_project.ymlと並んで欠かせないファイルです。
SQLiteアダプタのprofiles.ymlには、他のアダプタにはない少しクセのある設定項目があります。
| 項目 | 説明 |
|---|---|
type |
sqlite を指定 |
threads |
SQLiteは書き込み時にファイル全体をロックするため、並列数は1固定にする |
database |
SQLiteには「データベース」という概念がなく実際の動作には使われないが、設定上は必須。適当な文字列でよい |
schema |
SQLiteでは「最初に接続したファイル」が自動的にmainという名前のスキーマになる。ここはmainを指定する |
schemas_and_paths |
スキーマ名とSQLiteファイルパスの対応。mainには手順2で作ったraw.dbを指定する |
schema_directory |
dbtが新しくスキーマ(=ファイル)を作るときの保存先ディレクトリ |
これにより、手順2で作ったraw.dbをそのままmainスキーマとしてdbtから読み書きできるようになります。
dbt_hello_profile:
target: dev
outputs:
dev:
type: sqlite
threads: 1
database: 'database'
schema: 'main'
schemas_and_paths:
main: '/content/dbt_hello/raw.db'
schema_directory: '/content/dbt_hello'
実行結果(例):
Writing /content/dbt_hello/dbt_profiles/profiles.yml
# dbtに「profiles.ymlはこのディレクトリにあるよ」と教える
%env DBT_PROFILES_DIR=/content/dbt_hello/dbt_profiles
実行結果(例):
env: DBT_PROFILES_DIR=/content/dbt_hello/dbt_profiles
# 接続設定が正しいか確認する
%cd /content/dbt_hello/dbt_hello_project
!dbt debug
実行結果(例):
/content/dbt_hello/dbt_hello_project
02:52:02 Running with dbt=1.10.22
02:52:02 dbt version: 1.10.22
02:52:02 python version: 3.12.13
02:52:02 python path: /usr/bin/python3
02:52:02 os info: Linux-6.6.122+-x86_64-with-glibc2.35
02:52:02 Using profiles dir at /content/dbt_hello/dbt_profiles
02:52:02 Using profiles.yml file at /content/dbt_hello/dbt_profiles/profiles.yml
02:52:02 Using dbt_project.yml file at /content/dbt_hello/dbt_hello_project/dbt_project.yml
02:52:02 adapter type: sqlite
02:52:02 adapter version: 1.10.0
02:52:02 Configuration:
02:52:02 profiles.yml file [OK found and valid]
02:52:02 dbt_project.yml file [OK found and valid]
02:52:02 Required dependencies:
02:52:02 - git [OK found]
02:52:02 Connection:
02:52:02 database: database
02:52:02 schema: main
02:52:02 schemas_and_paths: {'main': '/content/dbt_hello/raw.db'}
02:52:02 schema_directory: /content/dbt_hello
02:52:02 Registered adapter: sqlite=1.10.0
02:52:02 Connection test: [OK connection ok]
02:52:02 All checks passed!
Connection test: [OK connection ok] と All checks passed! が表示されていれば、dbtからraw.dbに接続できる状態になっています。
5. モデルを書く(1): マスタとトランザクションをそれぞれクレンジングする
ここからが本題です。まずは、raw_customersとraw_ordersをそれぞれ軽くクレンジングする「staging」モデルを1つずつ作ります。生データのテーブルは、{{ ref(...) }}のような特別な記法を使わず、素のSQLでそのままFROM raw_customersのように参照します。
stg_customers: 顧客マスタの空白を取り除き(TRIM)、regionが未入力の行は'unknown'として扱い(COALESCE)、完全に重複した行は1件にまとめます(SELECT DISTINCT)。
select distinct
customer_id,
trim(customer_name) as customer_name,
coalesce(region, 'unknown') as region,
signup_date
from raw_customers
stg_orders: 注文トランザクションのうち、customer_idが分からない注文(NULL)と、金額がマイナスの不正な注文を除外します。
select
order_id,
customer_id,
product,
amount,
order_date
from raw_orders
where customer_id is not null
and amount > 0
最後に、データ品質のテストを追加します。customer_idやorder_idが重複していないか(unique)、NULLになっていないか(not_null)を、YAMLで宣言するだけで検証できます。
version: 2
models:
- name: stg_customers
columns:
- name: customer_id
tests:
- unique
- not_null
- name: stg_orders
columns:
- name: order_id
tests:
- unique
- not_null
- name: customer_id
tests:
- not_null
6. モデルを書く(2): ref()でマスタとトランザクションを結合する
stg_customersとstg_ordersという、きれいになった2つのモデルができました。ここからは、この2つを結合して、「顧客ごとの注文サマリ」を作ります。
ここで登場するのが{{ ref('モデル名') }}です。raw_customersやraw_ordersとは違い、stg_customersやstg_ordersはdbt自身が作ったモデルなので、ref()を使って参照します。ref()を使うと、dbtは「customer_order_summaryはstg_customersとstg_ordersの両方が完成してから実行する必要がある」という依存関係を自動的に把握します。実行順序をこちらで指定する必要はありません。
顧客マスタを主役にしてLEFT JOINすることで、まだ1件も注文していない顧客(Emi Sato)も、注文件数0件として結果に残るようにします。
select
c.customer_id,
c.customer_name,
c.region,
count(o.order_id) as order_count,
coalesce(sum(o.amount), 0) as total_amount
from {{ ref('stg_customers') }} as c
left join {{ ref('stg_orders') }} as o
on c.customer_id = o.customer_id
group by c.customer_id, c.customer_name, c.region
version: 2
models:
- name: customer_order_summary
columns:
- name: customer_id
tests:
- unique
- not_null
7. dbt run: SQLを組み立てて実行する
準備が整いました。dbt runを実行すると、dbtは.sqlファイルの依存関係(ref())を解決しながら、stg_customers → stg_orders → customer_order_summaryの順に実際のSQLを組み立てて、SQLiteに対して実行します。
%cd /content/dbt_hello/dbt_hello_project
!dbt run
実行結果(例):
/content/dbt_hello/dbt_hello_project
02:52:39 Running with dbt=1.10.22
02:52:40 Registered adapter: sqlite=1.10.0
02:52:40 Unable to do partial parsing because saved manifest not found. Starting full parse.
02:52:42 Found 3 models, 7 data tests, 416 macros
02:52:42
02:52:42 Concurrency: 1 threads (target='dev')
02:52:42
02:52:42 1 of 3 START sql view model main.stg_customers ................................. [RUN]
02:52:42 1 of 3 OK created sql view model main.stg_customers ............................ [OK in 0.06s]
02:52:42 2 of 3 START sql view model main.stg_orders .................................... [RUN]
02:52:42 2 of 3 OK created sql view model main.stg_orders ............................... [OK in 0.03s]
02:52:42 3 of 3 START sql table model main.customer_order_summary ....................... [RUN]
02:52:42 3 of 3 OK created sql table model main.customer_order_summary .................. [OK in 0.05s]
02:52:42
02:52:42 Finished running 1 table model, 2 view models in 0 hours 0 minutes and 0.24 seconds (0.24s).
02:52:42
02:52:42 Completed successfully
02:52:42
02:52:42 Done. PASS=3 WARN=0 ERROR=0 SKIP=0 NO-OP=0 TOTAL=3
3つのモデルすべてにOKが出ていれば成功です。raw.dbの中に、stg_customers・stg_ordersという2つのビューと、customer_order_summaryという結合済みのテーブルが新しくできています。
続けてdbt testで、先ほどYAMLに書いたテストを実行します。
!dbt test
実行結果(例):
02:52:51 Running with dbt=1.10.22
02:52:52 Registered adapter: sqlite=1.10.0
02:52:52 Found 3 models, 7 data tests, 416 macros
02:52:52
02:52:52 Concurrency: 1 threads (target='dev')
02:52:52
02:52:52 1 of 7 START test not_null_customer_order_summary_customer_id .................. [RUN]
02:52:52 1 of 7 PASS not_null_customer_order_summary_customer_id ........................ [PASS in 0.06s]
02:52:52 2 of 7 START test not_null_stg_customers_customer_id ........................... [RUN]
02:52:53 2 of 7 PASS not_null_stg_customers_customer_id ................................. [PASS in 0.02s]
02:52:53 3 of 7 START test not_null_stg_orders_customer_id .............................. [RUN]
02:52:53 3 of 7 PASS not_null_stg_orders_customer_id .................................... [PASS in 0.02s]
02:52:53 4 of 7 START test not_null_stg_orders_order_id ................................. [RUN]
02:52:53 4 of 7 PASS not_null_stg_orders_order_id ....................................... [PASS in 0.02s]
02:52:53 5 of 7 START test unique_customer_order_summary_customer_id .................... [RUN]
02:52:53 5 of 7 PASS unique_customer_order_summary_customer_id .......................... [PASS in 0.02s]
02:52:53 6 of 7 START test unique_stg_customers_customer_id ............................. [RUN]
02:52:53 6 of 7 PASS unique_stg_customers_customer_id ................................... [PASS in 0.02s]
02:52:53 7 of 7 START test unique_stg_orders_order_id ................................... [RUN]
02:52:53 7 of 7 PASS unique_stg_orders_order_id ......................................... [PASS in 0.02s]
02:52:53
02:52:53 Finished running 7 data tests in 0 hours 0 minutes and 0.29 seconds (0.29s).
02:52:53
02:52:53 Completed successfully
02:52:53
02:52:53 Done. PASS=7 WARN=0 ERROR=0 SKIP=0 NO-OP=0 TOTAL=7
すべてのテストがPASSしていれば、customer_id・order_idの重複やNULLがないことが確認できたことになります。
8. 仕上げ: dbtが発行したSQLと結果を自分の目で見る
最後に、「dbtが裏側で何をしていたのか」を実際のSQLで確認します。
まずdbt showコマンドで、結合済みモデルの実行結果をその場でプレビューします。
!dbt show --select customer_order_summary --limit 10
実行結果(例):
02:53:05 Running with dbt=1.10.22
02:53:05 Registered adapter: sqlite=1.10.0
02:53:06 Found 3 models, 7 data tests, 416 macros
02:53:06
02:53:06 Concurrency: 1 threads (target='dev')
02:53:06
Previewing node 'customer_order_summary':
| customer_id | customer_name | region | order_count | total_amount |
| ----------- | ------------- | ------- | ----------- | ------------ |
| 1 | Aya Tanaka | kanto | 3 | 1,430 |
| 2 | Bo Lee | unknown | 2 | 230 |
| 3 | cong wang | kansai | 0 | 0 |
| 4 | Dan Kim | kanto | 1 | 1,200 |
| 5 | Emi Sato | unknown | 0 | 0 |
次に、customer_order_summary.sqlの中で使った{{ ref(...) }}が、最終的にどんな生のSQLに変換されたのかを見てみます。dbtは実行のたびに、コンパイル後のSQLをtarget/compiled/以下に保存しています。
with open(
"/content/dbt_hello/dbt_hello_project/target/compiled/"
"dbt_hello_project/models/marts/customer_order_summary.sql"
) as f:
print(f.read())
実行結果(例):
select
c.customer_id,
c.customer_name,
c.region,
count(o.order_id) as order_count,
coalesce(sum(o.amount), 0) as total_amount
from main."stg_customers" as c
left join main."stg_orders" as o
on c.customer_id = o.customer_id
group by c.customer_id, c.customer_name, c.region
{{ ref('stg_customers') }}や{{ ref('stg_orders') }}と書いていた部分が、main."stg_customers"・main."stg_orders"という具体的なテーブル参照(=通常のSQLのJOIN)に置き換わっているのが分かります。dbtは、Jinjaテンプレートで書かれたモデルを、最終的にはこうした素のSQLへと変換してからデータベースに投げています。
最後に、Pythonから直接SQLiteファイルにSQLを発行して、結果を確認してみましょう。
import pandas as pd
con = sqlite3.connect(DB_PATH)
result = pd.read_sql(
"SELECT * FROM customer_order_summary ORDER BY total_amount DESC",
con,
)
con.close()
result
実行結果(例):
customer_id customer_name region order_count total_amount
0 1 Aya Tanaka kanto 3 1430.0
1 4 Dan Kim kanto 1 1200.0
2 2 Bo Lee unknown 2 230.0
3 3 cong wang kansai 0 0.0
4 5 Emi Sato unknown 0 0.0
結果を見ると、クレンジングと結合の効果が分かります。
-
customer_id=3(cong wang)は、マスタの重複登録が1件にまとめられた上で、金額がマイナスだった注文が除外されているため、order_count=0になっています。 -
customer_id=5(Emi Sato)は、そもそも注文が1件もありませんが、LEFT JOINのおかげでorder_count=0の顧客として結果に残っています。 -
regionが未入力だった顧客(Bo Lee、Emi Sato)は、'unknown'として扱われています。 - どの顧客にも紐付かなかった注文(
order_id=6)は、集計のどこにも登場しません。
生データそのままでは扱いづらかった2つのテーブルが、クレンジングと結合を経て、そのまま分析やレポートに使える1つのテーブルになりました。これで、Google Colab上だけで完結する形の「dbt Hello World」は完了です。