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

第一个模型:建立本地实验环境

目标:在本地运行一个 SQL 模型,并从 DuckDB 查到它生成的结果。

本课先让整个链条通一次:文件里的 SQL 经 dbt 变成数据库中的对象,再查询这个对象。SQL 模型写的是“需要哪些结果”;创建表或视图的操作由 dbt 按配置补上。我们先用默认的 view(视图),它保存查询定义,读取时计算结果;第 9 课再与表比较。

本实验使用 Python 版 dbt Core、连接 DuckDB 的 adapter(适配器),以及 DuckDB 本身。适配器负责把 dbt 的通用操作接到具体数据库。uv run 保证我们调用练习目录里的工具,即使电脑原来装了另一版 dbt,也不必混用。

先按 uv 官方安装说明安装 uv。本课命令面向 macOS、Linux 或 Windows WSL;无需事先安装数据库服务器或编写 Python。SQL、YAML 和 CSV 代码块请保存到指定文件,只有 bash 代码块在终端执行。

课程固定 dbt-core 1.12.5、dbt-duckdb 1.11.0 和 DuckDB 1.5.5。dbt Core 属于 Python 实现的 v1 系列;截至 2026 年 9 月,官网默认介绍的已是 Rust 实现的 v2。先沿用本课验证过的隔离环境,阅读官网时注意版本,不要混用 v2 安装命令。官方版本说明

建立一个独立的练习目录

先在你存放练习的父目录运行下面命令。不要放到课程源码目录里面。shop_dbt 若已存在,换一个空父目录再开始。

uv --version
uv init --bare --python 3.12 shop_dbt
cd shop_dbt
uv python pin 3.12
uv add 'dbt-core==1.12.5' 'dbt-duckdb==1.11.0' 'duckdb==1.5.5'
mkdir -p models/staging models/marts seeds tests macros
uv run dbt --version

安装需要网络,uv 会准备 Python 和隔离环境。预期版本信息包含 Core 1.12.5、duckdb adapter 1.11.0;“有更新可用”的提示不影响本实验。按本课程复制配置时使用该基线。

cd shop_dbt 把当前工作目录切到练习项目,uv python pin 3.12 创建 .python-version 来固定解释器系列,mkdir -p 创建后续所需目录。之后各课命令都在 shop_dbt 根目录执行;不必每课重新 init 或 add。首次安装会生成 uv.lock,锁住这次解析出的完整依赖,后续保留它以复现自己的环境。

创建文件 dbt_project.yml:

name: shop_dbt
version: '1.0.0'
config-version: 2
profile: shop_dbt
model-paths: [models]
seed-paths: [seeds]
test-paths: [tests]
macro-paths: [macros]

它告诉 dbt 项目叫什么、文件放在哪里,以及找哪个连接配置。config-version: 2 是项目配置格式,不表示使用 dbt v2 引擎。YAML 用缩进表达层级,使用空格,不用 Tab。

创建同目录下的 profiles.yml:

shop_dbt:
  target: dev
  outputs:
    dev:
      type: duckdb
      path: shop.duckdb
      schema: main
      threads: 1

profile 对应顶层 shop_dbt;target 选择 outputs 里的 dev。path 是本地数据库文件,schema 是库里组织表的命名空间。一个线程足够看清本课运行顺序。我们始终在项目根运行,所以相对路径没有歧义。用 --profiles-dir . 明确读取这里的配置;. 表示当前目录。

从一个 select 开始

创建 models/hello.sql,写入完整内容:

select 1 as id, 'hello dbt' as message

select 选出结果,as 给列起名。没有 from 时可以直接查询常量。先预测:结果会有几行、几列?再运行:

uv run dbt debug --profiles-dir .
uv run dbt run --profiles-dir . --select hello
uv run dbt show --profiles-dir . --inline "select * from {{ ref('hello') }}"

debug 检查配置与连接;run 构建模型;--select hello 只选这个模型。show --inline 用于预览查询结果。这里的 ref('hello') 先理解为“找到 hello 模型生成的对象”,第 4 课会展开。

预期输出的结果部分是:

id | message
1  | hello dbt

日志应显示成功建立一个 view,查询得到一行两列。shop.duckdb 是数据库数据,target/ 是编译等产物,logs/ 是日志;真正要维护的转换逻辑仍在 models/hello.sql。

改一个值,观察何时生效

  1. 把文件中的 'hello dbt' 改成 'hello shop',保存。
  2. 先只运行上面的 show 命令。仍是旧值,因为文件还没有交给数据库。
  3. 重新执行 run 和 show,现在应看到 hello shop。
  4. 恢复文件中的 'hello dbt',再运行 run。后续以这个版本为准。

独立练习:把模型临时改成输出 id=2、message='ready'。运行并核对结果,再恢复主版本。为什么只改文件不会改变查询结果?

参考实现与解释

models/hello.sql 的练习版完整内容为:

select 2 as id, 'ready' as message

仍运行本课的 run、show 两条命令,预期为 2 | ready。文件是转换定义,数据库里已有的视图使用上次构建的定义;dbt 不是监视文件并自动热更新的服务。

若提示找不到 profile,检查文件名、根键与 profile 三处是否一致;若 dbt 版本不符,确认命令前有 uv run,并在 shop_dbt 内执行。若出现数据库锁冲突,关闭另一个正在连接该文件的进程,顺序重试,不要同时跑两个 dbt 命令。

本课保留配置文件、models/hello.sql、uv 生成的 pyproject.toml、.python-version 与 uv.lock。以后不必重新初始化目录。

参考资料