dbt+SQLServer构建数据仓库(2):与传统方案对比及迁移决策

本文接上篇的 dbt 定位与工作流程, 讲述:dbt 和 SQL Server 上传统数仓开发相比有哪些范式差异和取舍

一、引言

上篇我们弄清了 dbt 是什么、一个 dbt 项目怎么跑起来。但"dbt 好不好"是个伪命题------好坏是相对的,得有参照系。这个参照系就是团队现在用的 SQL Server 传统数仓栈。

所以本文先描绘传统方案的典型形态和真实痛点,再从六个维度与 dbt 系统对比,最后给决策矩阵和迁移建议。本文不站队,目标是把两种范式的差异讲透,让你能根据团队情况做判断。

二、传统 SQL Server 数仓项目长什么样

2.1 典型技术栈

工具 作用
ETL SSIS (SQL Server Integration Services) 抽取、加载、转换一体化
转换 存储过程 (T-SQL) 复杂业务逻辑
调度 SQL Server Agent 定时跑 SSIS 包 / 存储过程
OLAP SSAS (Analysis Services) 多维模型 / 计算
报表 SSRS / Power BI 报表与可视化
IDE SSMS (SQL Server Management Studio) 主要开发环境
版本控制 数据库项目 + SSDT(可选) 较弱,很多人不用

2.2 典型开发流程

一个新需求的传统流程:

  1. 需求分析 → 确认指标定义、粒度、来源
  2. 设计 → 画 ER 图、定表结构、写设计文档
  3. 建表 → 在 SSMS 里 CREATE TABLE ...,DDL 散落各处
  4. 写存储过程 → 一个 sp_dim_customer_build 之类的过程,几百上千行 T-SQL
  5. 写 SSIS 包(如果需要跨源) → 拖拽式设计器,生成 .dtsx XML
  6. 配 Agent Job → 设置调度、告警、重试
  7. 手工测试 → 跑一遍,肉眼看数据对不对
  8. 部署 → 把存储过程脚本拿去 QA/Prod 库执行,SSIS 包导入 msdb
  9. 文档 → 单独写 Word/Confluence,很快和代码脱节

2.3 传统方案的真实痛点

这些不是理论问题,是项目里实打实遇到的:

  1. 依赖关系靠人记:存储过程 A 依赖 B 和 C,只有跑挂了才知道。改 B 之前要全公司发邮件问"谁在用 B"。
  2. 测试基本靠肉眼 :除了偶尔写 IF EXISTS 检查,数据质量靠业务方投诉发现。
  3. 版本控制形同虚设 :存储过程改在 SSMS 里直接 ALTER,历史靠数据库备份。SSIS 的 .dtsx 是二进制 XML,diff 几乎不可读。
  4. 环境不一致:Dev 库的存储过程版本和 Prod 不一样,迁移靠"复制粘贴 + 祈祷"。
  5. 文档秒级腐烂:Word 文档写完就过时,没人敢信。
  6. 重用靠复制:同样的逻辑在十个过程里复制十遍,改一处要改十处。
  7. 新人上手慢:理解一个数仓要先读 N 个存储过程的调用链,一周才能改一个字段。

三、对比分析:dbt vs 传统 SQL Server 方案

下面从六个维度系统对比。每个维度都给出两者的具体形态和取舍。

3.1 范式差异:声明式 vs 命令式

这是最根本的差异。

传统存储过程(命令式):

sql 复制代码
CREATE PROCEDURE sp_build_dim_customer AS
BEGIN
    TRUNCATE TABLE dim_customer;

    INSERT INTO dim_customer (customer_id, first_name, last_name, ltv)
    SELECT c.id, c.first_name, c.last_name, SUM(p.amount)
    FROM raw_customer c
    LEFT JOIN raw_order o ON c.id = o.user_id
    LEFT JOIN raw_payment p ON o.id = p.order_id
    WHERE p.status = 'completed'
    GROUP BY c.id, c.first_name, c.last_name;

    -- 手动写日志
    INSERT INTO etl_log (proc_name, rows_affected, run_at)
    VALUES ('sp_build_dim_customer', @@ROWCOUNT, GETDATE());
END

显式控制每一步:清表、插入、记日志、报错处理。灵活,但样板代码多,每个过程都要重复。

dbt model(声明式):

sql 复制代码
-- models/marts/dim_customers.sql
with customers as (select * from {{ ref('stg_customers') }}),
     payments as (
         select customer_id, sum(amount) as ltv
         from {{ ref('stg_payments') }}
         where status = 'completed'
         group by customer_id
     )
select c.customer_id, c.first_name, c.last_name,
       coalesce(p.ltv, 0) as ltv
from customers c
left join payments p on c.customer_id = p.customer_id
yaml 复制代码
# dbt_project.yml 里声明物化
models:
  my_project:
    marts:
      +materialized: table   # dbt 自动处理 truncate + insert

你只声明转换逻辑和物化意图,dbt 负责:清表、建表、依赖顺序、日志、错误处理。

取舍:

  • 声明式省心,但失去对细节的控制(如自定义事务、特殊锁提示)
  • 命令式灵活,但样板代码膨胀,易出错
  • dbt 仍可写 pre-hook/post-hook 注入命令式逻辑,两者并非水火不容

3.2 工程化能力对比

能力 传统方案 dbt
版本控制 SSDT 数据库项目(弱采用)或裸脚本 原生 git,每个 .sql/.yml 都是文本
依赖管理 人工维护调用链 ref() 自动解析 DAG
单元/数据测试 手写 IF EXISTS 检查 内建 unique/not_null/relationships/accepted_values + 自定义
CI/CD 几乎没有,靠手动部署 GitHub Actions/GitLab CI 跑 dbt build,PR 自动验证
文档 Word/Confluence,与代码脱节 dbt docs 从代码生成,含 DAG 血缘图
环境隔离 鄙视链:Dev < QA < Prod,迁移靠脚本 --target dev/prod + profile 切换,schema 前缀天然隔离
代码复用 复制存储过程,或写标量函数(性能差) macro 复用,编译期内联,无运行时开销
可重现性 同样的脚本在不同库可能跑出不同结果 同样的代码 + 同样的 seed → 完全一致

这一栏是 dbt 最大的优势所在。传统方案不是不能做这些事,而是每件都要自己搭:自己写测试框架、自己写文档生成器、自己搞 CI 脚本。dbt 开箱即用。

3.3 开发体验对比

维度 传统方案 (SSMS + 存储过程) dbt (CLI + 任意编辑器)
编辑器 SSMS(只能 Windows) VS Code / Vim / 任意编辑器(跨平台)
反馈速度 改完 → 部署 → 等调度 → 看结果(分钟~小时) dbt run --select x(秒级)
调试 PRINT 语句 + 临时表 dbt compile 看生成的 SQL + dbt show --inline
改动范围 全文搜索存储过程名找调用方 dbt ls --select x+ 一键列出所有下游
学习材料 MSDN 文档 + 公司内部 Wiki 官方文档 + 社区 + 大量博客/课程
团队协作 锁对象、合并冲突难 git 标准 workflow,PR review

开发体验的差异是最直观的。dbt 让数据工程师能像软件工程师一样工作------这是它能在创业公司和云原生团队迅速普及的根本原因。

3.4 性能与调度对比

维度 传统方案 dbt
调度器 SQL Server Agent(成熟、企业级、UI 全) 不内置,需外接(Airflow/dbt Cloud/cron)
并发执行 Agent Job 多步串行为主 threads: N 并发跑独立节点
增量加载 存储过程 + MERGE/IDENTITY 手写 materialized=incremental + unique_key 声明式
事务 完整 T-SQL 事务控制(默认) 默认 auto-commit,需 flag 启用 BEGIN/COMMIT
性能调优 索引、分区、列存、内存优化表全可控 同样可控,但需通过 post-hook/原生 SQL 注入
资源隔离 Agent Job 可绑 CPU/内存 依赖数据库侧的资源池

结论:

  • 调度成熟度:传统方案胜。SQL Server Agent 是企业级调度器,dbt 调度需外接
  • 转换性能:两者本质都是跑 T-SQL,差异不大。传统方案对底层控制更细
  • 增量加载:dbt 声明式更省心,但复杂增量场景(如多源 MERGE + 自定义逻辑)传统方案更灵活

3.5 SQL Server 方言适配

这是我曾经踩过的坑,值得单独提。

传统方案:直接写 T-SQL,方言就是方言,无适配问题。

dbt-sqlserver :dbt-core 是方言无关的,适配器层负责翻译。但 dbt-sqlserver 有几个 legacy 行为与 dbt-core 默认不一致:

行为 dbt-core 默认 dbt-sqlserver 默认 启用标准行为的 flag
schema 拼接 target.schema + '_' + custom custom 直接用(丢前缀) dbt_sqlserver_use_default_schema_concat: true
事务 emit BEGIN TRAN/COMMIT auto-commit,失败不回滚 dbt_sqlserver_use_dbt_transactions: true
字符串类型 STRING → VARCHAR(MAX) STRING → VARCHAR(8000) dbt_sqlserver_use_native_string_types: true

我在实际使用的时候,就因为第一个坑(schema 拼接)第一次 dbt run 报错。解决方案是在 dbt_project.yml 加:

yaml 复制代码
flags:
  dbt_sqlserver_use_default_schema_concat: true

启示 :跨适配器迁移时,generate_schema_name 这类 macro 是高风险点,务必审阅 dbt 输出的所有 WARNING。

3.6 学习曲线与团队适配

维度 传统方案 dbt
前置知识 T-SQL + SSIS + Agent(都是微软生态) SQL + Jinja + YAML + 命令行
学习曲线 SSIS 拖拽上手快,深水区深 dbt 本身简单,Jinja/macros 是进阶难点
团队背景匹配 DBA / 数据库开发熟悉的工具链 数据分析师 / 软件工程师熟悉的工具链
招聘 国内 SQL Server DBA 池子稳定但缩水 dbt 人才稀缺但增长快
培训成本 老团队零成本 老团队需 1-2 个月过渡
生态惯性 已有 SSIS 包 / Agent Job 的历史包袱 新项目零负担

关键判断:如果团队是 DBA 主导、已有大量 SSIS 资产,全面迁移 dbt 不现实;如果是数据工程师/分析师主导、新项目起步,dbt 几乎是默认选择。

四、何时选 dbt,何时选传统方案

基于上面的对比,给一个决策矩阵:

4.1 选 dbt 的场景

  • 新项目从零起步,没有历史包袱
  • ✅ 团队以数据分析师/工程师为主,熟悉 git/CLI
  • ✅ 数据已落库(ELT 模式),只需做 T
  • ✅ 重视工程化(测试、CI、文档、代码审查)
  • ✅ 未来可能跨云迁移(dbt 适配器机制让迁移成本可控)
  • ✅ 数据模型迭代频繁,需要快速试错

4.2 选传统方案的场景

  • 已有大量 SSIS 包 / 存储过程,迁移成本高于收益
  • ✅ 团队是 DBA / SQL Server 老兵,dbt 学习成本高
  • ✅ 需要重度 OLAP(SSAS 多维模型,dbt 不覆盖)
  • ✅ 调度复杂度高(SQL Server Agent 的告警、重试、作业链成熟)
  • ✅ 强依赖 SQL Server 特有功能(内存优化表、列存索引、PolyBase)
  • ✅ 合规要求严格,微软官方支持更稳妥

4.3 决策树

复制代码
是新项目吗?
├─ 是 → 团队背景?
│       ├─ 数据工程师/分析师为主 → dbt
│       └─ DBA 为主 → 传统方案 或 dbt(视学习意愿)
└─ 否(已有资产) → 资产规模?
        ├─ 小(<50 个过程/包) → 可考虑全量迁 dbt
        ├─ 中 (50-500) → 混合方案, 新需求用 dbt, 老的留着
        └─ 大 (>500) → 传统方案维持, 仅新模块评估 dbt

五、混合方案:现实中最常见的落地

实际项目里,纯 dbt 或纯传统方案都少见,混合方案是主流:

复制代码
┌─────────────────────────────────────────────────────┐
│  数据源 (MySQL / API / 文件)                         │
└─────────────────────────────────────────────────────┘
                    │
                    │  SSIS / Airbyte 做 E+L (老资产复用)
                    ▼
┌─────────────────────────────────────────────────────┐
│  SQL Server 原始库 (raw schema)                     │
└─────────────────────────────────────────────────────┘
                    │
                    │  dbt 做 T (转换层, 新方式)
                    │  dbt_project.yml + models/
                    ▼
┌─────────────────────────────────────────────────────┐
│  SQL Server 数仓层 (staging/marts schema)          │
└─────────────────────────────────────────────────────┘
                    │
                    │  SQL Server Agent / Airflow 调度 dbt
                    ▼
┌─────────────────────────────────────────────────────┐
│  Power BI / SSRS 消费                               │
└─────────────────────────────────────────────────────┘

落地策略:

  1. 保留 SSIS 做 E+L:已经稳定的抽取逻辑没必要重写
  2. 转换层切到 dbt:新需求一律用 dbt model,旧存储过程逐步迁
  3. 调度用 Agent 调 dbt :Agent Job 里跑 dbt run --target prod,复用成熟告警
  4. OLAP 层保持 SSAS:dbt 不覆盖多维模型,该用 SSAS 还用 SSAS
  5. BI 层不变:Power BI 看到的是表,不关心是存储过程还是 dbt 产的

这样既享受 dbt 的工程化红利,又不浪费已有投资。

六、迁移建议

如果团队决定从传统方案迁 dbt,建议:

6.1 渐进式,不要大爆炸

  • 选一个独立的小业务域(如某个部门的报表)做试点
  • 用 dbt 重建该域的转换,与老存储过程并行运行 1-2 个月
  • 数据对齐后切换 BI,下线老过程

6.2 先建分层,再迁逻辑

迁移前先在 dbt 里搭好 raw / staging / marts 三层骨架。老存储过程通常是"一锅炖"------清洗、聚合、业务逻辑混在一起。迁移时趁机分层,质量会提升一档。

6.3 把测试当一等公民

迁一个 model 就配一个 schema.yml 测试。传统方案缺测试,迁移是补测试的最佳时机。没有测试的迁移等于没迁------你无法证明新逻辑和老逻辑等价。

6.4 适配器 flag 一次配齐

为避免踩 schema 拼接的坑。建议 dbt-sqlserver 项目初始就把三个 flag 配上:

yaml 复制代码
flags:
  dbt_sqlserver_use_default_schema_concat: true
  dbt_sqlserver_use_dbt_transactions: true
  dbt_sqlserver_use_native_string_types: true

避免后续逐个踩雷。

6.5 调度平滑过渡

第一阶段:SQL Server Agent 调 dbt run(命令行包一层)

第二阶段:引入 Airflow / Dagster,统一调度新老资产

第三阶段:老存储过程逐步迁成 dbt model,最终下线 Agent Job

七、总结

把全文浓缩成几条:

  1. dbt 不是 SSIS 的替代品,而是"转换层"的现代化方案。E+L 仍可复用 SSIS,只把 T 切到 dbt。
  2. dbt 的核心价值是工程化:git / 测试 / 文档 / CI / DAG 自动解析,这些是传统 SQL Server 数仓最缺的。
  3. 传统方案的核心优势是企业级调度和方言原生:SQL Server Agent、SSAS、T-SQL 全功能,在微软生态里仍难替代。
  4. 混合方案是现实最优解:SSIS 做 E+L,dbt 做 T,Agent/Airflow 调度,SSAS 做 OLAP,Power BI 做展示。
  5. dbt-sqlserver 有 legacy 坑 (schema 拼接、事务、字符串类型),新项目务必在 dbt_project.yml 配齐三个 flag。
  6. 迁移要渐进:选小域试点,分层重建,测试先行,调度平滑过渡。

附:核心概念对照速查表

维度 传统 SQL Server 方案 dbt 方案
转换单元 存储过程 / 视图 model (.sql)
依赖管理 人工维护 ref() 自动 DAG
物化决策 手写 DDL +materialized 配置
数据测试 手写 IF EXISTS YAML 声明 + 内建 generic tests
增量加载 手写 MERGE materialized=incremental
历史拉链 手写 SCD2 表 + MERGE snapshot 资源
代码复用 复制 / 标量函数 macro (Jinja)
调度 SQL Server Agent 外接 (Airflow / dbt Cloud)
版本控制 SSDT(弱) git 原生
文档 Word/Confluence dbt docs 自动生成
IDE SSMS (Windows only) 任意编辑器 (跨平台)
调试 PRINT + 临时表 dbt compile + dbt show
环境隔离 多库实例 --target + schema 前缀
方言 T-SQL 原生 适配器翻译(有 legacy 差异)
OLAP SSAS 多维模型 不覆盖
报表 SSRS / Power BI 不覆盖(交给 BI 工具)

参考