未记录事实核验 · 状态:现行内容

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

warning 适用边界与维护说明

本文适用于已有可运行 dbt 项目的基础文档编写、文档块复用和已声明依赖分析,不是完整生产部署方案。独立 docs 目录须纳入项目文档扫描路径;模型与字段描述中的业务规则、币种、折扣口径及更新频率是示例内容,应按实际来源和模型实现确认,订单 ID 的格式与唯一性已在正文中明确限定为示例假设。CI/CD 片段仅为发布骨架,需另备连接配置、发布权限和 Pages 站点设置,并确认真实元数据的访问范围,避免内部表结构、描述或编译 SQL 被非授权读者访问。dbt 血缘及命令输出仅覆盖已声明依赖,项目外报表、任务和消费关系须另行确认。本次仅作全文结构审阅,未进行外部事实核验或命令运行验证。

dbt 文档与数据血缘 - 让数据可解释

abstract 本文核心

"这张表是什么意思?" "这个字段从哪里来的?" "我能信任这个数据吗?" 数据文档和血缘追踪是解决这三个问题的答案。 dbt 把文档和代码绑定在一起,自动生成可视化的数据目录和血缘图。


数据文档的真实价值

设想一个场景:

你接手了一个离职同事的项目,里面有几十张表。你打开数据库一看:

fct_orders
fct_orders_v2
fct_orders_new
fct_orders_final
dim_cust
dim_customer
customer_info
customer_data_cleaned

没有任何文档,不知道用哪张表,不知道字段含义,不知道哪个是最新的。

这就是为什么文档很重要——它把隐性知识变成显性知识,减少了信息不对称带来的团队摩擦。

为什么 dbt 的文档方式更好

传统文档的问题:

  • 文档和代码分离,很快就过期了
  • 没有人有动力维护
  • 找不到文档,或者找到的是旧版本

dbt 的做法:

  • 文档就在 schema.yml 里,和 SQL 代码放在一起
  • 每次 dbt docs generate 自动生成最新的网站
  • 血缘图从代码结构自动生成,不需要手动维护

在 schema.yml 中写文档

模型级别的文档

# models/marts/core/_core_models.yml

version: 2

models:
  - name: fct_orders
    description: |
      **订单事实表**

      每行代表一个客户订单,记录订单从创建到完成的完整信息。

      **粒度**:订单级别(order_id 唯一)
      **数据来源**:Shopify 电商平台
      **更新频率**:每小时增量更新
      **数据范围**:2020-01-01 至今

      **注意**:该表已过滤测试订单(financial_status = 'test')
      和已取消订单(status = 'cancelled')。
      如需分析退款,请查询 `fct_refunds`。

字段级别的文档

    columns:
      - name: order_id
        description: |
          订单唯一标识。
          来源:Shopify orders 表的 id 字段。
          格式:本示例假设为正整数、自增,且在所建模的订单数据范围内唯一;实际格式与唯一性须按来源验证,跨店铺或多来源关联需确认键空间,必要时使用组合键。

      - name: customer_id
        description: "客户 ID,外键,关联 dim_customers.customer_id"

      - name: financial_status
        description: |
          订单支付状态。可能的值:
          - `pending`: 已创建,等待支付
          - `paid`: 已支付
          - `refunded`: 已全额退款
          - `voided`: 已作废(未付款取消)

      - name: total_price_cents
        description: "订单总金额,单位:分(避免浮点精度问题)。例:29999 表示 299.99 元"

      - name: is_first_order
        description: "布尔值,标识该订单是否为该客户的第一个订单"

使用文档块(Doc Blocks)复用描述

当同一段描述需要在多个地方使用时,可以用 Doc Block 复用:

<!-- docs/column_descriptions.md -->

{% docs order_id %}
订单唯一标识。
来源于 Shopify 的 `orders.id` 字段。
本示例假设该 ID 在所建模的订单数据范围内唯一;实际跨表关联前应确认各表使用相同的 ID 键空间并验证唯一性,多店铺或多来源数据可能需要组合键。
{% enddocs %}

{% docs customer_id %}
客户唯一标识。
来源于 `dim_customers.customer_id`。
通过该字段可以关联客户的所有订单、偏好、画像等信息。
{% enddocs %}

在 schema.yml 中引用:

columns:
  - name: order_id
    description: "{{ doc('order_id') }}"    # 引用 doc block

  - name: customer_id
    description: "{{ doc('customer_id') }}"

tip Doc Block 的最佳用途

维度表和事实表会共享很多字段(如 customer_id)。 用 Doc Block 在一个地方维护这些字段的描述,避免多处不一致。


生成和查看文档

生成文档

# 生成文档网站(生成 target/catalog.json 等文件)
dbt docs generate

# 在本地启动文档网站(默认 http://localhost:8080)
dbt docs serve

# 指定端口
dbt docs serve --port 8888

文档网站的功能

生成的文档网站包含:

  1. 数据目录:所有模型、源表的列表和描述
  2. 字段详情:每个字段的类型、描述、测试
  3. 数据血缘图:可视化的依赖关系图
  4. SQL 查看:每个模型的编译后 SQL

数据血缘图

什么是数据血缘

数据血缘(Data Lineage)描述了数据从源头到最终产出的完整流动路径:

原始数据 → 清洗 → 关联 → 聚合 → 报表
    ↓         ↓       ↓       ↓       ↓
 Shopify   stg_    int_    fct_   BI 图表
 orders   orders  orders  orders

知道了血缘,你就能回答:

  • 这张表的数据从哪里来?
  • 这张表被哪些下游依赖?
  • 如果我改了 staging 层,会影响哪些报表?

dbt 如何生成血缘图

dbt 从 ref() 和 source() 函数自动推断已声明的血缘关系,无需手动绘制这些模型依赖;项目外的报表消费关系等需要另行声明或纳入治理记录。

在 dbt docs 界面中,选中一个模型,点击"Lineage Graph"可以看到:

[Shopify orders] ──→ [stg_orders] ──→ [int_order_items_joined] ──→ [fct_orders]
[Shopify customers] ──→ [stg_customers] ──┘                         │
                                           └──→ [dim_customers] ────┘

在命令行查看依赖

# 查看 fct_orders 的所有上游(--select +fct_orders 中 + 在左边)
dbt ls --select +fct_orders

# 查看 stg_orders 的所有下游(+ 在右边)
dbt ls --select stg_orders+

# 查看特定层的所有模型
dbt ls --select staging

# 输出为 JSON 格式(方便程序处理)
dbt ls --select +fct_orders --output json

将血缘与数据治理结合

数据血缘不只是一张好看的图,它是数据治理的核心基础设施。

影响分析(Impact Analysis)

当你需要修改一个模型时,血缘图告诉你影响范围:

# 如果我修改了 stg_orders,哪些下游会受影响?
dbt ls --select stg_orders+

# 输出:
# my_project.staging.stg_orders
# my_project.intermediate.int_order_items_joined
# my_project.marts.core.fct_orders
# my_project.marts.core.fct_order_items
# my_project.marts.finance.fct_daily_revenue

这个列表告诉你:如果你改了 stg_orders,需要同时测试这 4 个下游模型。

根因分析(Root Cause Analysis)

当报表数据异常时,从血缘图倒推:

fct_orders 数据异常
    → 检查上游 int_order_items_joined
        → 发现 stg_orders 的 status 字段处理有误
            → 追溯到 Shopify 源数据格式变更

血缘图让问题定位从"大海捞针"变成"沿线索追踪"。这与 数据血缘追踪 中的治理方法论是相互印证的。


description 最佳实践

要写什么

# 好的文档
- name: fct_orders
  description: |
    订单事实表。每行代表一个完成的客户订单。

    **粒度**:订单(order_id)
    **数据来源**:Shopify API via Fivetran
    **更新频率**:每小时增量

    **重要业务规则**:
    - 已取消订单不包含在此表中(如需分析取消原因,用 fct_cancellations)
    - total_price_cents 包含税费和运费(商品价值用 subtotal_price_cents)
    - 该表只包含 2020-01-01 之后的数据
# 不好的文档
- name: fct_orders
  description: "订单表"    # 废话,表名本身就是这个意思
# 更不好的:没有文档
- name: fct_orders
  # description 缺失

字段描述的模板

[字段含义描述]。
来源:[来自哪个原始表/字段]。
单位:[如果是金额/数量,说明单位]。
格式:[如果是编码/枚举,列出所有可能值]。
注意:[需要特别注意的业务规则或陷阱]。

示例:

- name: discount_amount_cents
  description: |
    订单的折扣金额,单位:分(人民币)。
    来源:Shopify orders.total_discounts,转换为分存储(原始值为元,×100)。
    注意:该值恒为正数(表示折扣金额),计算实付金额时需用 subtotal 减去此值。
    如果没有折扣,该字段为 0(不为 NULL)。

在 CI/CD 中发布文档

生产环境中,文档通常需要部署到内部网站供团队访问:

# .github/workflows/dbt-docs.yml
# 每次 main 分支更新时,自动重新生成并发布文档

name: Publish dbt Docs

on:
  push:
    branches: [main]

jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install dbt
        run: pip install dbt-core dbt-postgres

      - name: Generate docs
        run: |
          dbt deps
          dbt docs generate
        env:
          DBT_PROFILES_DIR: .
          DB_HOST: ${{ secrets.DB_HOST }}
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./target

总结

功能 实现方式 命令
模型文档 schema.yml 中的 description dbt docs generate
字段文档 columns[*].description dbt docs generate
复用描述 Doc Blocks(.md 文件) 引用 {{ doc('name') }}
数据血缘 自动从 ref() 推断 dbt docs serve 查看
影响分析 dbt ls --select model+ 命令行

下一步

文档和血缘搞定了,接下来学 dbt 的"超能力"——用 Jinja 模板让 SQL 更强大:

08-Jinja与宏 - 变量、条件、循环和宏,消灭 SQL 中的重复代码


最后更新:2026-10-09

#dbt #数据文档 #数据血缘 #数据治理 #数据目录


info 前置知识

  • 熟悉 schema.yml 的基本结构(已在测试章节中接触过),参考 测试与数据质量
  • 了解数据治理中"数据目录"和"数据血缘"的基本概念
  • 知道如何在命令行运行 dbt 基础命令

掌握检查

  • [ ] 能在 schema.yml 中为模型和关键字段写出有实际价值的 description(不是废话,而是能回答"这个字段是什么、从哪来、有什么注意事项")
  • [ ] 能用 dbt docs generate + dbt docs serve 在本地生成并查看文档网站,找到某个模型的血缘图
  • [ ] 理解 Doc Blocks(.md 文件 + {{ doc('name') }})的使用场景:当多个模型共用相同字段的描述时,避免重复维护
  • [ ] 知道 dbt ls --select model+ 命令的作用:列出某个模型及其所有下游模型(影响分析)
  • [ ] 能说出数据血缘图在数据变更评估中的价值:修改上游 staging 模型前,先用血缘图确认哪些下游 marts 会受到影响

实践练习

基础题:为你的 fct_orders.sql 在 schema.yml 中补充模型级别的 description(说明表的粒度、主要用途、数据时效性),以及 3 个核心字段的描述(参考本文的字段描述模板:含义、来源、单位、格式)。运行 dbt docs generate && dbt docs serve,在文档网站中验证描述是否正确显示。

进阶题:某个分析师问你"如果把 stg_orders.sql 中的 status 字段清洗逻辑修改了,会影响哪些下游模型?"。用 dbt ls --select stg_orders+ 列出 stg_orders 自身及 dbt 已声明依赖中的下游节点,识别其中受影响的模型,并在文档血缘图中核对结果;未纳入图中的外部消费关系需另行确认。说明你会如何与分析师沟通这次变更的影响范围。