Chapter 02

模型(Models)基础

dbt 的核心抽象——用 .sql 文件定义数据转换,用 ref() 构建模型间依赖

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(有向无环图),并按拓扑顺序执行:

raw.orders
stg_orders
int_orders_enriched
orders_summary

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 参数精确控制运行范围,支持 + 前/后缀选择上下游依赖。