Course Builder个人学习工作台 · 本地模式
dbt 入门:把 SQL 变成可信的数据流水线

通用数据测试:让坏数据停下来

目标:为订单和客户定义数据约束,并通过一次坏数据实验观察失败与恢复。

能运行的 SQL 不一定给出可信结果。例如客户 1 出现两次,上一课的连接就可能把他展示两次;订单指向客户 99,而客户表没有 99,报表可能漏掉金额。我们需要检查数据必须满足的条件。

本课使用 data tests,检查数据库里实际生成的数据。默认设置下,测试查询返回零条违规记录就通过,返回违规记录就失败。它们不自动修复记录,也不自动把约束变成数据库主键。unit tests 则是另一类功能,用人为构造的小输入检查模型逻辑,本课暂不展开。

四个内置通用测试各有用途:not_null 检查缺失;unique 检查重复;accepted_values 限定允许值;relationships 检查引用键能否在另一张表中找到。唯一与非空应分开声明,引用完整性也不能代替非空检查。

把假设写进 YAML

创建 models/staging/schema.yml:

version: 2
models:
  - name: stg_customers
    columns:
      - name: customer_id
        data_tests: [not_null, unique]
      - name: customer_name
        data_tests: [not_null]
  - name: stg_orders
    columns:
      - name: order_id
        data_tests: [not_null, unique]
      - name: customer_id
        data_tests:
          - not_null
          - relationships:
              arguments:
                to: ref('stg_customers')
                field: customer_id
      - name: status
        data_tests:
          - not_null
          - accepted_values:
              arguments:
                values: [placed, completed, returned]
      - name: amount_cents
        data_tests: [not_null]
      - name: updated_at
        data_tests: [not_null]

创建 models/marts/schema.yml:

version: 2
models:
  - name: customer_summary
    columns:
      - name: customer_id
        data_tests: [not_null, unique]

文件里的 name 必须与 SQL 文件名去掉扩展名后一致。arguments 是传给带参数测试的配置,例如允许哪些状态;按照本课版本使用这个写法,不要混入旧教程的顶层参数写法。

先预测正常样本是否满足每一项约束,再运行:

uv run dbt seed --profiles-dir .
uv run dbt build --profiles-dir . --exclude resource_type:seed
uv run dbt test --profiles-dir . --select stg_orders

此时共有 13 项通用数据测试,完整 build 应全部通过。最后一条命令只检查选择到的订单相关测试。test 不会重新构建模型;build 会把构建和测试放入同一依赖流程,因此上游错误级测试失败后,相关下游可以被跳过。

注入一个状态错误

  1. 把 seeds/raw_orders.csv 中 105 的状态 placed 改成拼错的 complete。
  2. 再运行 seed 和 build。accepted_values 测试应失败,customer_summary 应显示 SKIP。
  3. 展开日志中的测试名称,确认是状态约束,不是数据库连接故障。
  4. 将 complete 恢复为 placed,再次 seed 和 build,全部通过。

这里的 SKIP 表示这次没有成功构建下游,不表示上次生成的对象被删除。第 9 课还会看到:如果下游是视图,它读取变化后的上游时仍可能变化。不要把 build 当成“整个项目全部成功才一次性发布”的事务。

独立练习:临时把订单 105 的 customer_id 改成 99。预测哪个测试失败,运行验证,然后恢复为 3。

参考答案与恢复

relationships 应找到一条不匹配的订单,因为 stg_customers 中没有 99。99 既非空也不重复,并不会触发 order_id 的唯一性检查。恢复 CSV 中的 customer_id=3,重新执行:

uv run dbt seed --profiles-dir .
uv run dbt build --profiles-dir . --exclude resource_type:seed

要在日志中确认错误数回到零,而不是只看到前几个测试 PASS 就停止阅读。

若提示测试宏找不到或 YAML 解析失败,先核对缩进、data_tests 与 arguments 的层级;若修改 CSV 后测试仍通过,检查是否先 seed。不要为了让绿灯回来就删测试:本实验应该修复输入,只有业务规则真的改变时才讨论修改约束。

本课保留两份 schema.yml,CSV 恢复原始值。属性文件和 SQL 文件共同描述同一个模型,二者都应纳入代码管理。

参考资料