很多数据集成项目,最初关注的都是:数据库能不能连上、接口能不能调通、任务能不能按时跑完。真正上线以后才会发现, “数据搬过来了”只是第一关,“搬过来的数据能不能直接用”才是更难的一关。
同一个客户在CRM里有三条记录;ERP里的区域编码是01,销售系统里却写“华东”;订单金额出现空值,不知道是业务没填还是链路丢了;同步任务失败重跑一次,目标表突然多出几十万条重复数据。
这些问题看起来只是重复、空值和编码不一致,背后真正暴露的却是:不同系统对同一业务对象、同一字段和同一状态,没有使用同一套规则。 所以数据清洗绝不是简单的删重、补空和替换字符,而是要把分散在各系统里的业务规则重新统一起来。

一、重复数据:先判断“重复的到底是什么”
很多人碰到重复数据,第一个反应就是:DISTINCT。 但真实项目里,两行完全一样的数据反而最好处理。更麻烦的是下面几种情况。
同一笔业务被重复写入
例如订单同步任务凌晨执行一次,中途失败。开发人员重新跑任务后,第一次已经写成功的数据再次进入目标库,于是同一张订单出现两条甚至多条记录。这种重复的根源并不是源数据,而是同步链路缺少幂等控制。
真正需要控制的是:业务唯一键是什么;已经写入的数据再次到达时怎么办;任务失败以后从哪里恢复;重跑时覆盖哪一个时间范围。
例如订单表通常可以用订单号判断唯一性,支付流水则可能需要“支付单号+流水号”,库存流水甚至可能需要“单据号+行号+动作类型”。
所以唯一键不能只看数据库有没有主键,而要回到业务过程判断:到底什么组合能够唯一代表一次业务事实。

同一个业务对象出现多条记录
第二类更复杂。例如:张三,138xxxx1234;张三,+86 138xxxx1234;技术上是两条记录,但业务上可能是同一个客户。再比如:上海XX科技有限公司;上海XX科技有限公司(总部)。到底是同一家企业的两个名称,还是两个不同主体?这就不是简单SQL去重能够解决的了,而要建立实体识别规则。
通常可以按照可信度分层: 强标识字段:身份证号、统一社会信用代码、系统统一客户ID; 中等标识字段:手机号、邮箱、银行账号; 弱标识字段:名称、地址、联系人。强标识一致时可以直接合并;只有弱标识相似时,则不应该自动处理,而应进入待确认名单。

粒度变化造成的“伪重复”
订单表一单一行,订单明细表一件商品一行。两张表关联后,一张订单自然会出现多行。如果直接去重,很可能把正常商品明细删掉。所以判断重复前一定要先明确:当前表是一客户一行、一订单一行,还是一订单商品一行? 很多所谓“重复”,本质上其实是统计粒度没有定义清楚。
二、空值:不要急着补,先判断“为什么为空”
空值比重复更容易被误处理。因为很多团队会习惯性做:金额为空填0,名称为空填“未知”,日期为空填1900-01-01。结果表面上空值率下降了,业务含义却被改变了。NULL、0、“未知”和“未发生”完全不是一回事。

例如客户注销日期为空,可能代表客户仍然有效。订单发货时间为空,可能表示还没发货,也可能是物流数据没有同步回来。交易金额为空,则可能意味着系统异常,因为一笔已完成订单理论上不应该没有金额。
因此,空值至少要分成四类。
业务允许为空
例如备注、附件、注销时间。这类数据不需要修复。
业务应该有,但没有采集
例如客户行业、销售负责人。这属于数据录入质量问题,应该追到业务流程,而不是只在数仓里补一个默认值。
上游系统本来就没有
例如旧ERP没有客户等级字段,新CRM才新增这个字段。这类空值体现的是系统覆盖范围差异。

集成过程中产生的异常空值
源系统有值,目标表却为空。这种情况要重点排查:字段映射有没有错、JOIN条件是否匹配、字段类型转换有没有失败、上游结构是否发生变化。所以空值治理真正需要的不是“统一填值”,而是建立:字段重要程度 + 空值原因 + 处理方式
三者之间的关系。关键主键、交易金额、状态字段出现异常空值时,可以直接拦截;普通描述字段允许为空;能够根据明确业务规则推导的字段才考虑补齐。数据完整率不是越高越好,错误填充出来的100%完整率没有意义。

三、编码不一致:真正难的是标准如何长期维护
跨系统集成里,最常见的问题之一就是编码体系各自为政。同一个销售区域:ERP:01 CRM:EAST OA:华东区 历史系统:HD。如果直接汇总,系统会认为它们是四个不同区域。最开始系统不多时,开发人员往往直接写:CASE WHEN region='01' THEN '华东'
问题在于,这种方法很容易越写越多。半年以后可能变成:订单任务里一套地区转换,客户任务里一套,经营报表里又有一套。一旦业务把“华东区”拆成“华东一区”和“华东二区”,最危险的不是改规则,而是:你根本不知道旧规则散落在哪些地方。

所以成熟的编码治理通常会拆成三层。
第一层:定义标准编码
例如统一规定:华东一区 = R001 华东二区 = R002
第二层:建立来源映射
ERP 01 → R001 CRM EAST-A → R001 OA 华东一区 → R001
第三层:保留版本
这一点经常被忽略。假设2026年组织架构发生调整,如果直接覆盖旧映射,重新计算2025年的数据时,也可能套用2026年的组织关系。
因此映射表最好还要记录:生效时间、失效时间、来源系统、责任部门。 这样才能做到“历史数据按历史口径解释,当前数据按当前规则解释”。

四、清洗最危险的地方:把“异常数据”洗成“看起来正常”
很多数据项目有一个误区:只要最后数据里没有空值、没有重复、编码统一,就说明数据质量好了。实际上,错误的清洗规则可能比脏数据更危险。
例如:“客户名称相同就合并。”结果两家不同企业恰好同名。“地区为空统一补华东。”结果全国未知地区的订单全部进入华东。“销售额异常高就删除。”结果刚好删掉一个真实大客户订单。
因此数据清洗必须区分:确定错误、可以推断、无法判断。 确定错误的数据可以自动修复;能够依据明确规则推断的数据,可以自动转换;无法判断的数据应该进入异常区,而不是强行处理。这样做的关键不是“多存一张异常表”,而是保住了一条原则:清洗系统只处理自己能够确定的事情,不替业务做无法确认的判断。

五、真正成熟的数据清洗,要从“修数据”变成“管规则”
很多企业的数据质量工作长期处于救火状态。报表发现客户重复,补一段SQL;发现区域不一致,再加一层映射;出现空值,又临时补一个默认值。几年以后,真正麻烦的已经不是脏数据,而是:没人知道这些数据到底经过多少条规则。

因此,数据集成里的脏数据治理最终要形成四个环节。
标准
定义哪些字段不能为空,哪些编码合法,什么叫重复。
清洗
把标准转成可执行规则。
校验
任务跑完以后检查:主键重复率;关键字段空值率;非法编码数量;来源和目标记录数差异;数据量是否异常波动。因为:任务成功 ≠ 数据正确。 凌晨任务全部显示成功,但当天订单只有正常水平的30%,这批数据依然不能直接给业务使用。
追溯
保留原始值、处理规则、处理时间和修改结果。否则一旦数字出错,只能从最终报表反向猜测。

结语
重复、空值、编码不一致,看起来只是数据集成中的三个基础问题。实际上,它们分别在回答三个很重要的问题:重复数据解决的是“同一个业务对象到底是谁”;空值解决的是“数据缺失到底意味着什么”;编码不一致解决的是“不同系统怎样使用同一种业务语言”。
所以数据清洗真正的门槛,从来不是会不会写DISTINCT、COALESCE或者CASE WHEN。真正困难的是:企业能不能把分散在系统、代码和人员经验里的业务规则,逐渐变成统一、可维护、可校验、可追溯的数据规则。
低水平的数据清洗,是看到问题以后把值改掉。更成熟的数据集成,是先知道这个值为什么错,再决定谁来改、按照什么规则改,以及下一次怎样提前发现。 当这套机制建立起来,数据集成才真正从“搬运数据”,走向了持续生产可信数据。