第一个模型:建立本地实验环境
目标:在本地运行一个 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。
改一个值,观察何时生效
- 把文件中的
'hello dbt'改成'hello shop',保存。 - 先只运行上面的
show命令。仍是旧值,因为文件还没有交给数据库。 - 重新执行
run和show,现在应看到hello shop。 - 恢复文件中的
'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。以后不必重新初始化目录。