2.1 什么是 dbt 模型
dbt 模型(Model)是 models/ 目录下的一个 .sql 文件,其内容是一条 SELECT 语句。dbt 会自动在数据仓库中将这个 SELECT 语句物化(Materialize)为视图或表。
只写 SELECT,不写 CREATE
dbt 会自动根据物化配置生成 CREATE VIEW AS ... 或 CREATE TABLE AS SELECT ...。你只需专注于"数据应该长什么样",而非"如何创建它"。这是 dbt 最重要的设计决策之一。
最简单的模型示例
SQL-- models/staging/stg_orders.sql
SELECT
order_id,
customer_id,
order_date,
UPPER(status) AS status,
amount / 100.0 AS amount_dollars,
_loaded_at AS loaded_at
FROM raw.orders
运行 dbt run 后,dbt 会在数据仓库中创建一个名为 stg_orders 的视图(默认物化方式)。
2.2 ref() 函数——自动依赖追踪
ref() 是 dbt 最重要的函数。它引用另一个 dbt 模型,并自动建立依赖关系,确保被引用的模型先于当前模型执行。
SQL-- models/marts/orders_summary.sql
-- 引用 stg_orders 模型,而非直接写表名
SELECT
customer_id,
COUNT(*) AS total_orders,
SUM(amount_dollars) AS total_revenue,
MIN(order_date) AS first_order_date,
MAX(order_date) AS last_order_date
FROM {{ ref('stg_orders') }}
WHERE status = 'COMPLETED'
GROUP BY customer_id
ref() 的魔力
dbt 会将 {{ ref('stg_orders') }} 编译为实际的表/视图全限定名(如 my_db.dbt_dev.stg_orders)。这意味着同样的代码在 dev/staging/prod 环境运行时,会自动指向对应 schema 的表,无需手动修改表名。
依赖 DAG 的自动构建
dbt 扫描所有 ref() 调用,自动构建模型间的依赖 DAG(有向无环图),并按拓扑顺序执行:
2.3 source() 函数——引用原始数据
当需要引用数据仓库中的原始数据(非 dbt 模型)时,使用 source() 函数,而非直接硬编码表名。source() 搭配 schema.yml 使用(详见第3章)。
SQL-- models/staging/stg_orders.sql
-- 使用 source() 代替硬编码的 raw.orders
SELECT
order_id,
customer_id,
order_date,
status,
amount
FROM {{ source('ecommerce', 'orders') }}
-- source('source名称', '表名')
ref() vs source()
ref() 引用其他 dbt 模型(已转换的)
source() 引用原始数据源表(未经 dbt 处理的)
为什么不直接写表名? 使用 source() 允许 dbt 追踪血缘、做 source freshness 检查,并在切换环境时自动调整 schema 前缀。
2.4 物化类型(Materialization)
物化类型决定 dbt 如何在数据仓库中创建并持久化模型。每种类型有不同的性能特征和使用场景:
| 物化类型 | 数据库对象 | 每次 dbt run | 适用场景 |
|---|---|---|---|
view | 视图(View) | 重建视图定义 | 轻量转换、staging 层 |
table | 物理表 | 删除并重建全表 | 查询频繁、下游有多个消费者 |
incremental | 物理表 | 只插入/更新新数据 | 大表、有时间戳的日志数据 |
ephemeral | 不创建对象(CTE) | 内联为 CTE | 临时中间计算,不需要持久化 |
配置物化类型的三种方式
SQL-- 方式 1:在 SQL 文件顶部用 config() 配置(优先级最高)
{{ config(materialized='table') }}
SELECT * FROM {{ ref('stg_orders') }}
YAML# 方式 2:在 schema.yml 中为单个模型配置
models:
- name: orders_summary
config:
materialized: table
YAML# 方式 3:在 dbt_project.yml 中为整个目录设置默认值(优先级最低)
models:
my_analytics:
staging:
+materialized: view
marts:
+materialized: table
2.5 执行模型:dbt run
BASH# 运行所有模型
dbt run
# 只运行指定模型
dbt run --select stg_orders
# 运行一个模型及其所有上游依赖(+ 前缀)
dbt run --select +orders_summary
# 运行一个模型及其所有下游依赖(+ 后缀)
dbt run --select stg_orders+
# 运行 staging/ 目录下的所有模型
dbt run --select staging
# 运行多个指定模型
dbt run --select stg_orders stg_customers
# 排除某个模型
dbt run --exclude stg_orders
查看编译后的 SQL
dbt run 前会先将 Jinja 模板编译为原生 SQL,存放在 target/compiled/ 目录。可用 dbt compile 只编译不执行,方便调试。
2.6 实战:构建电商 staging 层
以一个电商系统为例,构建三个 staging 模型:
SQL-- models/staging/stg_customers.sql
{{ config(materialized='view') }}
SELECT
customer_id,
TRIM(first_name) AS first_name,
TRIM(last_name) AS last_name,
LOWER(email) AS email,
phone,
created_at
FROM {{ source('ecommerce', 'customers') }}
WHERE customer_id IS NOT NULL
SQL-- models/staging/stg_orders.sql
{{ config(materialized='view') }}
SELECT
order_id,
customer_id,
order_date::date AS order_date,
UPPER(status) AS status,
amount / 100.0 AS amount_dollars,
_loaded_at AS loaded_at
FROM {{ source('ecommerce', 'orders') }}
SQL-- models/marts/customer_orders.sql
-- mart 层:面向业务的宽表,物化为 table
{{ config(materialized='table') }}
WITH customers AS (
SELECT * FROM {{ ref('stg_customers') }}
),
orders AS (
SELECT * FROM {{ ref('stg_orders') }}
),
order_stats AS (
SELECT
customer_id,
COUNT(*) AS total_orders,
SUM(amount_dollars) AS lifetime_value,
MIN(order_date) AS first_order_date,
MAX(order_date) AS most_recent_order_date
FROM orders
GROUP BY customer_id
)
SELECT
c.customer_id,
c.first_name,
c.last_name,
c.email,
COALESCE(os.total_orders, 0) AS total_orders,
COALESCE(os.lifetime_value, 0) AS lifetime_value,
os.first_order_date,
os.most_recent_order_date
FROM customers c
LEFT JOIN order_stats os USING (customer_id)
本章小结
dbt 模型是 models/ 目录下包含 SELECT 语句的 .sql 文件,dbt 自动将其物化为数据仓库中的视图或表。
ref('model_name') 引用其他 dbt 模型并自动建立 DAG 依赖;source('source', 'table') 引用原始数据源表。物化类型:view(默认,轻量)、table(全量物理表)、incremental(增量)、ephemeral(内联 CTE)。
dbt run 执行全部模型,--select 参数精确控制运行范围,支持 + 前/后缀选择上下游依赖。