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

环境与运行流程:隔离开发和生产练习

目标:让相同项目分别构建到开发与生产练习数据库,并使用失败即停止的运行脚本。

开发时你会改 SQL、注入坏数据,不应该直接影响别人正在读取的结果。环境隔离的核心是让同一套代码指向不同目标。我们用两个本地文件模拟 dev 和 prod;这不是部署到真实生产服务器。再准备一个尚未使用的 ci 目标,让下一课验证空库重建。

profile 保存连接方式,target 选择其中一组连接参数。代码里的 ref 会按目标解析对象;source 在本项目中省略 database,因此同样使用当前目标数据库,但仍指向我们声明的 main schema。

给每个环境一份数据库

完整替换 profiles.yml:

shop_dbt:
  target: dev
  outputs:
    dev:
      type: duckdb
      path: shop.duckdb
      schema: main
      threads: 1
    prod:
      type: duckdb
      path: shop_prod.duckdb
      schema: main
      threads: 1
    ci:
      type: duckdb
      path: shop_ci.duckdb
      schema: main
      threads: 1

各输出的 schema 名相同,但数据库文件不同,所以对象相互隔离。仅把 target 名字改成 prod、却仍用相同 path,并不能隔离数据。本课配置没有秘密;真实数仓连接可能涉及凭据,应该通过相应环境的安全配置注入,不能照抄成本地文件后提交密钥。

预测首次查询 prod 是否已有报表。它是新的数据库,所以需要从装载开始:

uv run dbt debug --profiles-dir . --target prod
uv run dbt seed --profiles-dir . --target prod
uv run dbt build --profiles-dir . --target prod --exclude resource_type:seed
uv run dbt show --profiles-dir . --target prod --inline "select sum(revenue_cents) as cents from {{ ref('customer_summary') }}"

预期总额是 5200 分,全项目 17 项测试通过。注意每条命令都指定 prod;若 seed 到 dev 却 build 到 prod,新环境会缺原始表。

用一段脚本固定执行顺序

创建 run_pipeline.sh:

#!/usr/bin/env bash
set -euo pipefail
dbt_target="${1:-dev}"
uv sync --locked
uv run dbt debug --profiles-dir . --target "$dbt_target"
uv run dbt seed --profiles-dir . --target "$dbt_target"
uv run dbt build --profiles-dir . --target "$dbt_target" --exclude resource_type:seed
uv run dbt docs generate --profiles-dir . --target "$dbt_target"

${1:-dev} 读取第一个参数,没有提供时使用 dev;set -euo pipefail 让未定义变量、命令失败等情况停止脚本。uv sync --locked 按已生成的锁文件同步工具环境,若依赖声明与锁不一致则报错。后面依次检查连接、模拟上游装载、构建并测试、生成文档。

bash run_pipeline.sh dev
bash run_pipeline.sh prod

两次均应成功。docs 的 target 目录会保留最近一次生成的文档,若需要不同环境同时对外提供文档,应另外保存各自构建产物。本课仅本地预览,不发布。

验证隔离确实发生了

  1. 把 CSV 中 101 的金额改成 1300,同时把 updated_at 改成 2026-01-04 10:00:00,让已有增量表也能识别更正。
  2. 只运行 bash run_pipeline.sh dev。
  3. 分别查询两个环境:
uv run dbt show --profiles-dir . --target dev --inline "select sum(revenue_cents) as cents from {{ ref('customer_summary') }}"
uv run dbt show --profiles-dir . --target prod --inline "select sum(revenue_cents) as cents from {{ ref('customer_summary') }}"

应分别为 5300、5200。相同 CSV 在项目里变化了,但未执行 prod 装载与构建,因此 prod 仍保留上次结果。

  1. 把 CSV 恢复第 3 课五行原始数据。先 uv run dbt seed --profiles-dir . --target dev,再 uv run dbt build --profiles-dir . --target dev --exclude resource_type:seed --full-refresh。这会重置第 12 课解释过的增量水位;prod 没有被修改。

独立练习:如果脚本中的 build 失败,docs generate 是否应该执行?只执行一次成功的脚本,明天是否会自动重跑?

参考答案

不会继续生成文档,因为脚本在失败命令处退出。可以在开发 CSV 中暂时把 placed 写成 complete,运行脚本,确认日志停在 build 失败处,再恢复输入并重新运行。失败前成功执行的任务并不会自动回滚。

脚本没有定时功能。定时器或任务编排系统可以启动它,但需要另外明确运行环境、上游装载完成条件、退出状态和通知方式。本课程没有替你建立定时任务。真实生产流程通常由独立采集系统装载 source,不会每次把交易数据当 seed 重装。

若两个环境一起变化,先查是否误用了同一个 path;若 prod 找不到 source,检查装载命令的 target;若 locked 同步失败,检查是否修改过 pyproject.toml 却没有同步锁文件。不要在自动运行时默默升级依赖。

保留三目标 profile 与脚本,ci 尚未运行。需要提交的输入包括 models、seeds、tests、macros、项目配置、依赖声明与锁文件;.venv/、target/、logs/ 和数据库文件属于环境或生成结果。这里的本地无凭据 profile 可以作为教学配置;真实团队应按凭据管理规则处理。

参考资料