简介:ETL(Extract-Transform-Load,即数据抽取、转换与加载)是商业智能(BI)项目中最关键、最耗时的环节,通常占据项目约三分之一的时间。这份Word文档紧紧围绕ETL设计三大部分展开:数据抽取前的业务调研及同源/异源数据库、文件型数据源、增量更新的接入方法;数据清洗时针对不完整、错误、重复三类脏数据的过滤及用户确认流程;数据加载的常用工具(OWB、DTS、SSIS等)与SQL实现方式的选型要点。同时补充了不一致数据统一、数据粒度聚合、商务规则计算,以及ETL日志与告警发送等实战细节。压缩包为单个docx文件,大小20KB,内容紧凑,适合数据仓库工程师、BI开发人员及初学者快速搭建ETL设计框架。该资源已有1534人学习下载,可帮助读者理解从源系统到ODS再到DW的数据流转全链路,提前规避数据质量与加载效率方面的常见问题。
1. ETL 为什么吃掉 BI 项目三分之一的时间:问题先于方案
ETL 是 BI 项目里最容易被低估的环节。很多团队在需求阶段把精力花在报表样式和指标口径上,等到联调才发现数据从业务系统到仓库的链路根本没打通。这份 ETL 设计详解讲的就是这条链路:数据从哪来、怎么抽、抽完怎么洗、洗完怎么转、最后怎么落进 DW,以及出错时如何第一时间定位。ETL 通常占项目总时间的 1/3,其中清洗转换又占 ETL 的 2/3。这不是效率低,而是脏数据程度、口径差异和增量更新的复杂度,只有接过数据仓库项目的人才体会得到。适合正在做 BI 项目但还没系统化梳理过 ETL 的开发者,刚接手数仓建设的数据工程师,以及要评审 ETL 方案的技术负责人。
2. 数据抽取:四种数据源接入法与增量更新的时间戳陷阱
数据抽取的设计,调研阶段的工作量往往决定后续开发能不能顺利。动手之前先回答五个问题:数据来自哪几个业务系统、各系统的 DBMS 是哪种、有没有手工数据、有没有非结构化数据、接口人是谁。这几个问题问不清楚,后面抽到一半发现数据源少了一个,或者抽出来的字段口径对不上,返工成本远超想象。
2.1 同构数据源:DBLink 直连的建法与权限边界
与数据仓库同库型的数据源处理起来最省事。DBMS 一般会提供数据库链接功能,在数仓服务器和业务系统之间建立一条直接连接,写 Select 语句就能跨库访问。以 Oracle 为例,数仓库创建一个指向业务库的 database link,抽取语句直接写成SELECT ... FROM xxx@BIZ_LINK,不需要中间文件,也没有额外同步环节。
-- 以 Oracle 为例,在数仓库创建指向业务库的 database link CREATE DATABASE LINK BIZ_LINK CONNECT TO biz_user IDENTIFIED BY biz_pwd USING 'tns_name_biz'; -- 抽取时直接跨库查询,这里的时间戳就是 2.4 节要讲的水位线 INSERT INTO ods.ods_supplier SELECT * FROM biz_user.supplier@BIZ_LINK WHERE update_time >= :last_watermark;这里有两个细节。第一,抽取语句里的:last_watermark是上一轮抽取的水位线,通常由 ETL 调度系统传入,也可以先从 ODS 表查出来再拼进 SQL;第二,连接用的账号权限要单独控制,业务库那边只开放需要的表和字段,不要用最高权限账号做抽取。同构直连的优点是没有数据落地,抽得快,也不占存储,但前提是网络链路得稳定,跨机房连接超时之类的坑后面会遇到。
2.2 异构数据源:ODBC 桥接与文件落地的取舍
跨库型异构数据源,比如 SQL Server 和 Oracle 之间,一般能通过 ODBC 建立链接。配置好 ODBC 数据源之后,目标库就能把源库的表当成远程对象来访问。建不了数据库链接的情况同样存在,常见做法有两种:第一种是用数据库客户端工具把源数据导成 .txt 或 .xls 文件,再导入 ODS;第二种是写程序接口,通过中间服务把数据拉出来。
| 处理方式 | 前置条件 | 实时性 | 适用场景 |
|---|---|---|---|
| ODBC 建链直连 | 双方网络通、驱动匹配 | 较高 | 两个库在同一个内网 |
| 导出文件再导入 | 有导出权限即可 | 低 | 库权限受限、网络隔离 |
| 程序接口调用 | 源系统能提供 API | 中 | 实时性要求较高、数据结构复杂 |
ODBC 直连首选,文件落地是保底方案。做文件落地时注意字符集和分隔符,导出的 txt 如果字符集不一致,导入后中文乱码是常事,这个在清洗阶段处理起来非常吃力。
2.3 文件型数据源:业务人员导入还是工具组件
文件型数据源(.txt、.xls)主要来自业务人员手工维护的数据,比如区域编码表、客户补充信息表。处理路径有两条。一条是让业务人员先把文件导入到指定的临时库表,再从这里做后续抽取。另一条是借助 ETL 工具的平面组件,SSIS 里的平面数据源和平面目标就是干这个的,把源文件直接映射进 ODS,跳过手工入库这一步。
实际项目里我更推荐工具方式,因为手工入库步骤越多,失控的概率越大。文件格式、列名变更、编码混用,任何一个环节出问题都是脏数据源头。SSIS 平面组件可以把文件当作表来读,然后直接写入 ODS,流程可控,出错时也能在日志里定位到是哪一行解析失败。
2.4 增量更新:时间戳水位线的正确姿势
数据量小的表可以每次全量抽取,数据量大就必须考虑增量。最常见的做法是用业务系统记录的时间字段作为标志:每次抽取前先查出 ODS 中当前最大时间,再拿这个时间去业务系统取大于该时间的记录。
-- 抽取前先取 ODS 中当前最大时间作为水位线 SELECT MAX(update_time) AS last_watermark FROM ods.ods_supplier; -- 用上一轮水位线去业务库取增量 INSERT INTO ods.ods_supplier SELECT * FROM biz_link.supplier@BIZ_LINK WHERE update_time > :last_watermark;这个方案有两个容易翻车的地方。第一,时间字段要选对。update_time和create_time含义不同,如果业务系统只有创建时间而没有更新时间,那记录被修改后就漏抽了;第二,时间粒度问题,业务系统时间的精度如果只到秒,同一秒内的多条更新可能被上一次抽取截断。我一般会同时保留一个小于号,再用主键去重兜底,或者把水位线往前回拨 1~2 秒。如果业务系统干脆没有时间戳,就得评估全量抽取加对比快照的方案,或者上 CDC(变更数据捕获),这属于进阶话题,后面避坑章节再展开。
2.5 手工数据与非结构化数据:调研阶段容易漏掉的两类
调研阶段,团队往往只盯着几个核心业务系统,手工数据和非结构化数据容易被忽略。手工数据指那些没有系统支撑、由业务人员用 Excel 维护的数据,量大时不能靠人工慢慢敲库。非结构化数据,比如合同扫描件、留言文本,理论上不进常规维度表,但如果要分析文档元数据或文本内容,需要提前设计抽取方案。
这两类数据有一个共同特征:没有稳定的接口,也没有统一的数据字典。最保守的做法是先要求业务部门按期提供规范格式文件,固定列名和内容类型,再按 2.3 节的平面组件方式导入。格式不规范的文件第一轮就要打回,不要替业务人员做数据整理——清洗的职责可以向前延伸,但不能无限延伸到源文件编辑。
提示:抽取设计阶段至少要列出「每张源表的数据量、日增多少、是否有更新时间戳」三件套清单,拿这个清单去评估全量还是增量,才不会被数据量打措手不及。
3. 数据清洗转换:三类脏数据、三种转换规则与两段式分工
三部分里花费时间最长的是 T(清洗、转换),一般占整个 ETL 的 2/3。这个比例背后是脏数据的现实:源系统的数据质量通常低于预期,主数据不统一,历史数据有空档,字段格式千奇百怪。把清洗转换设计好,后面分析阶段才不用天天解释数据为什么对不上。
3.1 两段式分工:ODS 清洗、DW 转换
标准做法是从业务系统到 ODS 做清洗,把脏数据和不完整数据过滤掉;再从 ODS 到 DW 做转换,执行业务规则计算和聚合。两段式分工的意义在于各司其职:ODS 层解决"数据能不能信"的问题,DW 层解决"数据怎么用"的问题。如果清洗和转换混在一起做,逻辑会越缠越复杂,排起错来无从下手。
ODS 层清洗任务包括:过滤不完整记录、识别错误数据、处理重复行。DW 层的转换任务包括:统一不一致编码、把明细数据聚合到分析粒度、把复杂业务指标计算好再入库。清洗是减法,转换是加工,先后顺序不能反。
3.2 不完整数据:补全、退回还是过滤
不完整数据指应该有的信息缺失了。常见场景:供应商名称缺失、分公司名称没填、客户区域信息为空、业务系统主表与明细表对不上。这类数据不能直接丢,也不能直接入库,要先过滤出来分类处理。
-- 找出明细表里没有对应主表的孤儿数据 SELECT d.order_id, d.line_no, d.product_code FROM ods.ods_detail d LEFT JOIN ods.ods_master m ON d.order_id = m.order_id WHERE m.order_id IS NULL;处理流程是这样的:把缺失内容按类别分别写入不同的 Excel 文件,提交给客户确认,规定时间内补全后再抽取写入数仓。补全之前不要硬塞进 DW,否则后面分析汇总时这些半截数据会带出各种奇怪的口径偏差。这个流程要讲清楚一个原则:清洗结果是给业务部门确认的,不是清洗团队自己拍板丢弃。每一条过滤规则都要保留证据,能追溯到是哪张表、哪个字段、什么原因被过滤。
3.3 错误数据:全角字符、日期越界与隐藏空白
错误数据的产生原因一般是业务系统录入时缺少校验,数据直接落库。比如数值字段被输成全角数字字符、字符串末尾带着回车符、日期格式不正确、日期越界。这一类数据有个特点:用眼睛看不出问题,得用 SQL 精心排查。
-- 找出全角数字字符:全角数字的 Unicode 码位在 65296 到 65305 之间 SELECT * FROM ods.ods_supplier WHERE UNICODE(SUBSTRING(phone, 1, 1)) BETWEEN 65296 AND 65305; -- 检查日期越界记录 SELECT * FROM ods.ods_sale_order WHERE order_date > '9999-12-31' OR order_date < '1900-01-01';全角字符和前后不可见字符这类静态脏数据,只能靠 SQL 定期扫描,然后交由业务单位在源系统中修正后再抽取。日期格式不正确和越界会导致 ETL 在写入时报错甚至中断,这一类要优先处理,最好在任务调度时加一个前置检查脚本,每天跑批前先扫一遍日期字段。换句话说,错误数据分两类处理:静态格式问题可以后台清洗修正,但会引发运行失败的问题要拦截在流程入口。
3.4 重复数据:维表去重是重灾区
重复数据在事实表中出现的概率相对低,但维度表中非常常见。维度表如果存在重复记录,事实表关联时会出现一对多,汇总结果直接翻倍。
-- 按业务主键检查重复记录 SELECT supplier_code, COUNT(*) AS dup_cnt FROM ods.ods_supplier GROUP BY supplier_code HAVING COUNT(*) > 1;查出重复记录后,把重复记录的所有字段导出来,交客户确认保留哪一条。不要自行决定删哪条,尤其不要按入库顺序保留第一条,因为不同时间点的记录可能代表不同的真实状态。维表去重一旦出错,下游所有报表都会跟着错,而且很难排查到根因。
3.5 三种数据转换:编码统一、粒度聚合与业务规则计算
清洗完成后进入转换阶段。第一种转换是不一致数据转换,把不同业务系统中的同一实体统一编码。同一个供应商在结算系统编码是 XX0001,在 CRM 系统编码是 YY0001,抽取过来就得映射成同一个主数据编码。第二种转换是数据粒度转换。业务系统存的是明细流水,数据仓库做分析不需要那么细,要按仓库的粒度聚合。第三种是商务规则计算,某些指标不是简单加减就能得到,要把复合指标在 ETL 中先算好再入库。
-- 按数据仓库粒度聚合月度销售金额 SELECT product_key, DATEADD(month, DATEDIFF(month, 0, order_date), 0) AS month_start, SUM(amount) AS total_amount FROM ods.ods_sale_order GROUP BY product_key, DATEADD(month, DATEDIFF(month, 0, order_date), 0);这段 SQL 的核心是把订单日期先归一到月份起点,再按产品和月份汇总。粒度聚合的关键在于先定好 DW 的粒度定义,是按日、按月还是按产品维度,因为粒度错了一切指标都白算。业务规则计算建议做成可配置的公式表,不要写死在代码里,不同企业对某个指标的定义经常三个月就变一次。
3.6 清洗规则验证:宁可多筛一次也别误杀
清洗最怕的是把有用的数据过滤掉了。每条过滤规则上线前,用反向逻辑验证一下:把被过滤的数据按原表保存一份,人工抽查比例不低于 5%。另外过滤规则本身要写注释,标明提出人、确认人、生效日期。我见过因为一个"金额大于 0"的过滤规则,把大量合法的冲正和退款记录干掉了,直接导致月度报表数据对不上,返工了一周才查出来。规则是给业务用的,不是给 ETL 开发自己用的,必须有确认记录。
注意:过滤规则不要一上来就全量启用。先离线跑一周,每天把过滤结果和人工判断对比一次,连续一周准确率达标后再并入正式调度。
4. 实现方式选型:纯工具、纯 SQL 与混合方案的边界与代价
ETL 的实现方法主流有三种:借助 ETL 工具、纯 SQL 方式、ETL 工具加 SQL 结合。选择时考虑的不只是开发速度,还有后期运维的难度。工具和 SQL 各有边界,盲目站队都是给自己挖坑。
4.1 纯 ETL 工具:屏蔽编码但牺牲灵活性
ETL 工具(如 Oracle OWB、SQL Server 的 DTS/SSIS、Informatica 等)能快速搭建工程,拖拽式的开发界面屏蔽了复杂编码,降低了对开发人员的技术门槛。这类工具适合复杂定时调度和大量数据源接入的场景,图形化流程一目了然。
但工具的短板同样明显,封装程度越高,灵活性越差。遇到工具封装不了的取数逻辑,就要回到写脚本或自定义组件,这往往比一开始用 SQL 更费劲。另外工具生成的日志对细节的暴露程度各不一样,排错时经常要翻工具自带的执行日志,有的日志不标明影响行数,定位问题只能靠数据比对。纯工具方案适合源表结构稳定、转换逻辑不复杂的项目,越快上线越好。
4.2 纯 SQL 方式:灵活高效但编码复杂
纯 SQL 方式主要是用存储过程和视图实现抽取、转换、加载的全链路,灵活度高,运行效率也容易控制。SQL 的优点是能精确控制每步逻辑,索引、分区、并行调优都在掌握之中,性能瓶颈容易定位。缺点是编码复杂,对开发人员的要求比较高,而且所有逻辑都堆在存储过程里时,维护成本会递增。
纯 SQL 适合两种场景:一是数据量很大而且逻辑复杂,用工具拼装反而别扭;二是团队对源数据库非常熟悉,能写出高性能的查询。但完全不用工具,调度怎么办?调度本身还是得借助调度平台或者服务器计划任务,这部分也算半个工具职能,不能算严格意义的纯 SQL。新手团队如果直接上纯 SQL,前期开发效率会比较低,排错也容易陷入层层调用的泥潭。
4.3 工具 + SQL 混合:生产环境最常见的架构
第三种方式在前两者之间找一个平衡:抽取用工具的连接器来做,复杂的转换逻辑下沉到 SQL 存储过程,加载再用工具的数据目标组件完成。这是我认为生产环境最值得推荐的架构,它的核心原则是分而治之——工具负责调度和流转,SQL 负责业务逻辑。
常见分工是:第一步用 ETL 工具的调度功能定时触发任务;第二步由工具的数据源组件抽取源表或执行 SQL 命令;第三步调用存储过程完成清洗和转换;第四步用工具把结果写入 DW。这样每步的职责清晰,工具的图形界面管全流程,SQL 管数据处理的核心部分,两头兼顾。存储过程里的业务规则变更可以直接改 SQL 重新部署,不必动整个流程,也不影响工具的调度稳定性。
4.4 三种方式对比表与决策参考
| 维度 | 纯 ETL 工具 | 纯 SQL | 工具 + SQL 混合 |
|---|---|---|---|
| 开发速度 | 快 | 慢 | 中等 |
| 灵活性 | 受限 | 最强 | 较强 |
| 对技术人员要求 | 低 | 高 | 中等 |
| 后期维护成本 | 依赖工具版本 | 依赖代码规范 | 依赖整体架构 |
| 适用场景 | 快速交付、逻辑简单 | 性能敏感、复杂计算 | 企业级长期项目 |
选型没有绝对标准,但我有两条血泪经验:交付周期三个月以内的项目,优先工具;周期半年以上、指标口径频繁变化的项目,必须给业务规则留 SQL 的活口。这两条踩过的人心里都有数。
5. 避坑清单:ETL 实施中五个最常见的翻车场景
ETL 的坑不是设计出来的,是运行出来的。下面五个问题都是项目里反复出现的典型,按「现象 → 原因 → 解决」记录,照着排查能省不少时间。
5.1 全角字符和隐藏回车:JOIN 不上时先查 ASCII
现象:两张表按编码关联,看数据都差不多的编码,但 JOIN 出来的记录数就是不对,明明存在的数据关联不上。
原因:源系统在录入时用了全角字符,或者字符串末尾带了一个回车符。视觉上看不出来,但底层字节已经变了,关联条件匹配不上。
解决:用 SQL 把可疑字段的 ASCII 码打印出来。全角数字的 Unicode 码位在 65296 附近,回车的 ASCII 是 13。发现后先按 3.3 节的方式扫一遍全表,把问题记录挑出来反馈业务方修正,不要试图在 ODS 层硬替换,因为脏数据源头还在,修正后还会继续产生。
5.2 日期越界导致 ETL 中断:跑批前做一次数据体检
现象:ETL 任务半夜跑批,跑到一半报错退出,日志显示是日期字段转换失败。第二天业务看数发现颗粒无收。
原因:源系统的日期字段要么是格式不正确,要么日期越界,比如超过 9999-12-31。写入 DW 时数据库类型转换直接抛异常。
解决:在调度流里加一个前置检查步骤,每天跑批前先扫描所有日期字段,发现异常记录立即输出到预警表并中断流程。宁可今天少更新一次,也不要让整个任务挂掉导致所有下游依赖一起失败。日期越界的修复原则是回源系统修正,不是 ETL 这边手工改。
5.3 业务系统没有时间戳:增量设计的终极难题
现象:数据量已经到百万千万级,没有办法每天全量抽取。但业务系统没有记录更新时间,增量无从下手。
原因:开发期调研没有确认好源系统是否支持增量抽取,部分老旧系统的表连更新时间字段都没有,只有插入时间。
解决:分三步走。第一,和业务方确认这个表是否只增不改,如果是,创建时间就是天然的增量标志;第二,如果存在修改,评估源库是否支持开启 CDC,能开就按日志增量来;第三,都不支持就只能每天全量加比对,将全量快照存放在 ODS 临时区,和昨日快照做对比后只写差异行。三个方案按成本递增,但都能绕开"没有时间戳"这个死结。
5.4 清洗规则过严:把有效数据过滤掉的误操作
现象:某天的过滤记录数量突增,但业务方反馈当天业务一切正常。手工核对后发现大量合法数据被清洗规则拦掉了。
原因:规则制定时以某一份数据样本为准,样本覆盖不到的类型全被当成脏数据过滤。最常见的是金额字段,负数和 0 被当成异常,但实际业务里冲正、退款、赠送、折扣都是合法场景。
解决:每条过滤规则都要做反向验证,把被过滤数据落表,和业务方确认哪些类型是合法数据,然后调整规则白名单。更重要的是规则本身要有版本记录,上线时间、修改人、业务确认人齐全,否则下次同样的问题还会再现。
5.5 维表重复数据:事实表关联结果翻倍
现象:报表里某个大类的销售总额突然翻了一倍,明细对账却对得上,总数就是不对。
原因:维表里出现两条相同的业务主键记录,事实表 JOIN 维表时一对多匹配,每条事实记录被拆成多行参与汇总。
解决:按业务主键 GROUP BY 查出重复项,全部导出交业务方裁决保留哪一条。在 DW 层修复之后,还要在 ODS 抽取层加一个去重逻辑,确保同一轮抽取内不会写进两条相同主键。事实表侧不用改,因为问题源头在维表。
提示:这几个问题都有一个共性,链路中任何一环的结果都需要被记录和被比较。没有日志和预警机制,翻车往往要等业务方发现,而不是 ETL 自己发现。
6. 日志与警告:把 ETL 从黑匣子变成可追踪的白盒
ETL 是一套长期运行的系统,运行不畅时最怕的就是不知道发生在哪一步、影响多少行。设计 ETL 时把日志和警告发送作为一等公民嵌入,运行期的焦虑能少一大半。
日志分三类:执行过程日志、错误日志和总体日志。执行过程日志以流水账形式记录每步运行内容,包括起始时间、结束时间、影响行数,这是定位性能瓶颈的核心依据。错误日志记录出错模块、出错时间和出错详情,方便回溯失败现场。总体日志只记录整个 ETL 作业的开始时间、结束时间、是否成功,用来做日常健康监控。用一句话概括:过程日志回答"干了什么",错误日志回答"哪里挂了",总体日志回答"这次跑没跑完"。
| 日志类型 | 记录内容 | 用途 |
|---|---|---|
| 执行过程日志 | 每步起始时间、影响行数、耗时 | 性能分析与数据量追踪 |
| 错误日志 | 出错时间、出错模块、错误信息 | 失败原因定位 |
| 总体日志 | 作业开始、结束、成功与否 | 整体运行状态监控 |
执行过程日志不要吝啬记录影响行数,同一张表今天抽了 10 万行、明天抽了 3 行,这个变化本身就是数据质量问题的信号。错误日志要落表,不要只写到工具自带日志里,因为工具日志散落在不同节点,排查起来效率低。有了这三类日志,ETL 运行情况几乎是透明的。
警告发送同样不能省。ETL 出错了,单靠日志不够,还要主动通知管理员。最常见的做法是发送邮件,附上具体出错模块与错误信息,便于管理员第一时间排查。邮件内容按"谁在什么时间跑什么任务,失败在哪个模块,错误详情是什么"的结构写,减少沟通成本。
从那以后,我每次搭建 ETL 都强制把日志表和警告发送放在第一步做,哪怕业务规则还没开发完,日志框架先立起来。数据量大不大、逻辑复杂不复杂,都不是理由。等真正出问题了,你会发现跑在日志前面的人都是在赌运气。希望帮到你。
本文还有配套的精品资源,点击获取