











「dbt+SQLServer构建数据仓库」是一套基于真实项目的实战学习笔记,记录我从零开始在 SQL Server 上用 dbt 搭建分层式数仓的完整过程。本文是第 1 篇,回答两个问题:dbt 到底是什么、一个 dbt 项目从开发到上线经历哪些环节。
SQL Server 在国内企业数仓里占有率很高,围绕它的传统数仓栈已经非常成熟:SSIS 做 ETL、存储过程做转换、SQL Server Agent 做调度、SSAS 做 OLAP、SSRS/Power BI 做报表。这套栈运行了二十年,稳定可靠。
但近年 dbt 异军突起,把"数据转换"这件事重新定义成了软件工程问题。很多团队在问:
要回答这些对比性问题,得先理清 dbt 的定位和工作方式。本文专注于把 dbt 本身讲透,对比和迁移决策留到下篇。
dbt 是一个让数据分析师用软件工程的方式编写数据转换的命令行工具。
关键词拆解:
pip install dbt-core 装好就能跑。┌──────────────────────────────────────────────────────────┐
│ 数据源: MySQL / Oracle / API / 文件 / 日志 │
└──────────────────────────────────────────────────────────┘
│
│ E (Extract) + L (Load)
│ 由 Fivetran / Airbyte / 自研 ETL 负责
▼
┌──────────────────────────────────────────────────────────┐
│ 数据仓库 / 数据湖: SQL Server / Snowflake / BigQuery │
│ (原始数据已落库) │
└──────────────────────────────────────────────────────────┘
│
│ T (Transform) ← dbt 在这里!
▼
┌──────────────────────────────────────────────────────────┐
│ 转换后的数据: dim/fact 表, 供 BI 消费 │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ BI 工具: Power BI / Tableau / Looker / Metabase │
└──────────────────────────────────────────────────────────┘
关键认知:dbt 不替代数据库,也不替代 ETL 工具,它只替代"在数据库里写转换逻辑"这一段。所以 dbt 是增量引入而非全盘替换,这是它相对 SSIS 的重要差异——具体对比见下篇。
| 概念 | 含义 | 传统 SQL Server 类比 |
|---|---|---|
| model | 一个 .sql 文件 = 一个转换 |
一个存储过程 / 一个视图 |
| ref() | 引用另一个 model,自动建依赖 | 手写 INSERT INTO ... SELECT FROM ... |
| source() | 声明外部源表 | 直接 FROM schema.table |
| materialization | 物化策略(view/table/incremental) | 手动决定建视图还是建表 |
| test | YAML 声明数据质量校验 | 手写 IF EXISTS ... RAISERROR |
| snapshot | SCD2 历史拉链表 | 手写 MERGE + 历史表 |
| macro | 可复用 Jinja 片段 | 标量函数 / 动态 SQL |
| seed | CSV 加载成表 | BULK INSERT / BCP |
这套概念体系是 dbt 的"语法骨架",后续所有工程化能力(测试、文档、CI)都建立在这之上。
dbt 的日常开发是一个紧凑的反馈环:
┌─────────────────────────────────────┐
│ 1. 写/改 model SQL (含 ref/source) │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 2. dbt run --select <model> │ ← 只跑改动的模型
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 3. dbt test --select <model> │ ← 验证数据质量
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 4. 查结果 / 看编译 SQL / 改 schema │
└─────────────────────────────────────┘
│
└──→ 回到 1
这个循环通常每人每天跑几十次,每次几秒到几十秒。与传统"改存储过程 → 部署 → 等调度 → 看结果"的小时级反馈相比,体验是数量级的提升(反馈速度的详细对比见下篇第五节)。
一个 dbt 项目从诞生到上线,经历这几个阶段:
dbt init my_project # 或手动建目录
# 配置 ~/.dbt/profiles.yml
dbt debug # 验证连接
产物:dbt_project.yml + 目录骨架。
按分层架构写 model:
models/
├── staging/ ← 1:1 投影源表, 重命名+类型
├── intermediate/ ← (可选) 中间计算
└── marts/ ← 业务维度/事实表
每个 model 是一个 .sql 文件,用 {{ ref('xxx') }} 串起来。dbt 解析所有 ref() 自动生成 DAG。
在 schema.yml 里声明测试:
models:
- name: dim_customers
columns:
- name: customer_id
tests: [unique, not_null]
dbt test 自动生成校验 SQL 并执行。失败的测试会阻断 CI(如果配了)。
dbt docs generate
dbt docs serve
生成一个可点击的网站,含模型描述、字段说明、DAG 血缘图。文档从代码生成,永远和代码同步。
dbt 本身不调度,部署方式有三种:
dbt run 在服务器上 cron 跑(简单项目)dbt run(企业主流)dbt run --store-failures:测试失败的行存到表里供排查target/run_results.json:拿运行时长、状态做监控大盘dbt run 内部发生了什么理解 dbt 的执行模型,有助于和传统方案对比。当你敲下 dbt run:
1. 解析阶段 (parse)
├─ 读 dbt_project.yml + 所有 .sql/.yml
├─ 解析 ref() / source() 依赖
└─ 构建 DAG (有向无环图)
2. 规划阶段 (plan)
├─ 拓扑排序 DAG
├─ 根据 --select 过滤要跑的节点
└─ 按 threads 并发分组
3. 编译阶段 (compile)
├─ Jinja 渲染 {{ ref('x') }} → 实际 schema.table
├─ 适配器方言转换 (SQL Server 的 T-SQL)
└─ 写入 target/compiled/
4. 执行阶段 (execute)
├─ 通过 adapter (pyodbc) 连数据库
├─ 按 DAG 顺序执行: create view / create table as ...
├─ 记录每个节点的状态/时长
└─ 写入 target/run_results.json
关键点:第 3 步编译是 dbt 的核心魔法——你写的 SQL 是"模板",dbt 把它编译成目标库的真实方言。这就是为什么同一个 dbt 项目能在 SQL Server / Snowflake / BigQuery 之间相对容易地迁移。
本文把 dbt 的定位和工作流程讲清楚了:
run --select + test --select 每天几十次,体验远超传统方案。dbt run 内部四步(parse → plan → compile → execute)中,compile 阶段的方言编译是跨库迁移的关键。但这只是 dbt 单方面的故事。要决定"要不要用 dbt",还得把它和团队现有的 SQL Server 传统方案摆在一起逐维度对照——这正是下一篇要做的事。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。