4. dbt内置通用测试详解
dbt 的通用测试(generic tests)是数据质量工程中最常用的一层防线。它们以 YAML 声明的方式挂在模型或列上,由 dbt 在 dbt test 时自动编译成 SQL 查询并执行。理解这四类内置测试的语法、配置、执行机制与适用场景,是构建可靠数据管道的基础。本节将逐一拆解 not_null、unique、accepted_values、relationships,并说明它们在实际项目中的组合方式。
通用测试的声明位置与执行机制
通用测试写在模型的 YAML 文件(通常是 schema.yml 或 _models.yml)中,通过 tests 键挂载。dbt 会为每个测试生成一条独立的 SQL 查询,查询返回的每一行都代表一次失败。若查询返回 0 行,测试通过;返回任意行,测试失败并列出违规记录。这种“返回失败行”的机制是理解所有通用测试的钥匙——测试的本质是“找出不满足约束的数据”。
执行时,dbt 会并行运行这些测试查询,并在日志中输出 PASS/FAIL 状态。失败时可通过 --store-failures 将失败行写入数据库表,便于排查。下面这张图展示了四类测试在数据流中的位置与各自的检查逻辑。
not_null:最基础的完整性约束
not_null 用于检查指定列是否存在 NULL 值。语法非常直接:
models:
- name: stg_orders
columns:
- name: order_id
tests:
- not_null
执行时 dbt 生成类似 SELECT * FROM stg_orders WHERE order_id IS NULL 的查询。若返回任何行,说明存在空值,测试失败。适用场景包括:主键列、外键列、金额列、时间戳列等业务上不允许缺失的字段。需要注意的是,not_null 只检查 NULL,不检查空字符串或 0,这些需要额外测试或自定义测试覆盖。
unique:主键与去重保障
unique 检查列值是否唯一。语法与 not_null 一致:
- name: order_id
tests:
- unique
- not_null
dbt 生成的查询为 SELECT order_id, COUNT(*) FROM stg_orders GROUP BY order_id HAVING COUNT(*) > 1。返回重复值即失败。实际项目中,unique 和 not_null 几乎总是成对出现在主键列上,二者组合才能完整表达“主键非空且唯一”的约束。对于复合主键,dbt 内置测试无法直接表达,需要借助 dbt_utils.unique_combination_of_columns 或自定义测试。
accepted_values:枚举与业务规则校验
accepted_values 检查列值是否落在允许的集合内。它需要传入 values 参数:
- name: status
tests:
- accepted_values:
values: ['pending', 'shipped', 'delivered', 'cancelled']
生成的查询为 SELECT * FROM stg_orders WHERE status NOT IN ('pending','shipped','delivered','cancelled')。任何不在集合中的值都会导致失败。适用场景包括:订单状态、支付方式、渠道类型、性别等有限枚举字段。配置时需注意大小写敏感和前后空格问题,建议在源数据清洗阶段统一格式。若允许 NULL 值,需额外说明——NOT IN 对 NULL 的处理会导致 NULL 行不被返回,因此 accepted_values 默认不检查 NULL,NULL 是否合法应由 not_null 单独控制。
relationships:跨表引用完整性
relationships 用于检查当前表的某列值是否全部存在于另一张表的指定列中,即外键约束。它需要 to 和 field 两个参数:
- name: customer_id
tests:
- relationships:
to: ref('dim_customers')
field: customer_id
dbt 生成的查询为:
SELECT o.customer_id
FROM stg_orders o
LEFT JOIN dim_customers c
ON o.customer_id = c.customer_id
WHERE c.customer_id IS NULL
返回的行即为“在订单表中存在、但在客户维度表中找不到对应记录”的孤儿外键。适用场景包括:事实表到维度表的外键、子表到父表的引用、任何需要保证引用完整性的关联字段。配置时需确保 to 使用 ref() 或 source(),以便 dbt 正确解析依赖关系并安排执行顺序。
四类测试的对比与组合策略
| 测试 | 检查目标 | 关键参数 | 典型适用列 | 失败含义 |
|---|---|---|---|---|
not_null |
非空 | 无 | 主键、外键、金额、时间 | 存在缺失值 |
unique |
唯一 | 无 | 主键、业务编号 | 存在重复值 |
accepted_values |
枚举合法 | values |
状态、类型、渠道 | 出现未定义枚举值 |
relationships |
引用完整 | to、field |
外键列 | 存在孤儿记录 |
unique 和 not_null;外键列挂 not_null 和 relationships;枚举列挂 accepted_values,若业务不允许空值再叠加 not_null。这样四类测试就能覆盖数据质量中最常见的完整性、唯一性、合法性和引用完整性四个维度。
accepted_values 不会捕获 NULL,因为 NOT IN 对 NULL 返回 UNKNOWN。若枚举列不允许空值,必须同时配置 not_null。另外,relationships 的目标列若存在重复值,测试仍可能通过,但引用完整性语义要求目标列本身唯一,建议在目标表上也配置 unique 测试。