SQL数据清洗实战:重复值、空值和异常值怎么处理?
企业做数据分析时,最容易被低估的环节,往往不是建模,也不是做可视化,而是数据清洗。
同一个客户被录入三次,手机号前后带空格,订单金额出现负数,日期字段中混入“暂无”,已经取消的订单仍被计入销售额……这些问题看起来只是几行脏数据,一旦进入指标计算,就可能造成客户数虚高、销售额重复、平均值失真、部门数据无法核对。
更麻烦的是,很多数据问题并不会直接报错。
SQL可以正常执行,报表也能正常刷新,但最终结果是错的。等业务部门发现数字异常,再回头排查数据源、加工逻辑和统计口径,往往已经消耗了大量时间。
所以,数据清洗不是简单删除错误记录,而是按照明确的业务规则,把原始数据转换成完整、一致、准确、可追溯的数据。
正式进入实操前,我整理了一份《数据仓库建设解决方案》,覆盖数据集成、数据治理、数据质量和数据应用等内容,适合企业梳理数据处理体系时参考。
需要自取:https://s.fanruan.com/7igmg(复制到浏览器)
一、写SQL之前,先把清洗规则定义清楚
很多人拿到数据后的第一反应,是直接写DELETE、UPDATE、DISTINCT或COALESCE。
但SQL只能执行规则,不能替企业定义规则。
例如,一张客户表中存在两个姓名相同、手机号相同,但客户编号不同的记录。它们可能是重复录入,也可能是同一联系人代表两家公司。如果没有结合企业主体、证件号码、所属公司和交易记录判断,直接删除其中一条,就可能破坏真实业务关系。
因此,正式清洗前,至少要明确四件事。
1、什么数据属于错误?
数据异常不一定等于数据错误。
订单金额为0,可能是测试数据,也可能是赠品订单;发货日期晚于计划日期,可能是录入错误,也可能是真实延期;一个手机号对应多个客户,也可能是家庭成员、门店共用号码或者企业联系人。
所以,清洗规则必须结合业务场景定义,不能只看字段值是否“正常”。
2、数据问题应该怎样处理?
常见处理方式并不只有删除,还包括:
修改为正确值;
保留原值并增加异常标签;
合并多条记录;
将异常记录写入隔离表;
暂时置空,等待业务补充;
保留记录,但不进入指标计算。
能够确定错误原因的数据,可以自动修复;无法确定真实含义的数据,应先标记和隔离,而不是直接覆盖。
3、清洗规则作用在哪一层?
不建议直接修改原始数据表。
更稳妥的做法是建立分层结构:
原始层:完整保留源系统数据;
清洗层:执行格式统一、去重、空值处理和异常校验;
应用层:向报表、指标和分析模型提供标准数据;
异常层:保存未通过规则、需要人工核查的数据。
这样做的价值在于,一旦清洗逻辑出现问题,还能回到原始数据重新计算,而不是把原始事实永久修改。
4、清洗结果怎样验证?
每条清洗规则都要设计验证指标,例如:
去重前后分别有多少条记录;
删除或合并了哪些业务对象;
空值率下降了多少;
异常记录占比是多少;
清洗前后金额合计是否一致;
主表和明细表能否正常关联。
面对ERP、CRM、Excel、数据库等多类数据源时,零散SQL脚本很容易出现执行顺序混乱、规则重复和任务无人维护的问题。
在FineDataLink中,可以把数据接入、SQL处理、字段转换、异常过滤和结果输出编排成完整任务链路,再设置定时调度和失败提醒,让清洗规则从“个人脚本”变成可重复执行的数据流程。
二、重复值处理:不是去掉相同行,而是识别同一业务对象
很多人处理重复数据时,会直接使用:
但DISTINCT只能去掉每个字段都完全相同的记录。
真实业务中的重复数据,通常并不会完全一致。
例如:
| 客户编号 | 客户名称 | 手机号 | 地址 | 更新时间 |
| C001 | 华星公司 | 13800000000 | 上海市 | 2026/7/1 |
| C089 | 华星有限公司 | 13800000000 | 上海 | 2026/7/15 |
两条记录字段不同,但很可能属于同一客户。
因此,去重的第一步不是删除,而是确定业务唯一键。
不同对象的唯一性判断方式不同:
订单通常按订单号判断;
员工通常按工号判断;
商品通常按商品编码判断;
客户可能按统一社会信用代码判断;
缺少统一编码时,可能需要使用“名称+手机号+地址”等组合字段。
识别重复记录,可以使用窗口函数:
这段SQL表示:按照手机号分组,将更新时间最新的一条记录标记为1,其余记录作为重复候选。
但“保留最新一条”并不一定永远正确。
如果旧记录填写了完整地址,新记录只有手机号;或者旧客户编号已经关联大量订单,新记录却没有历史交易,那么简单保留最新记录,反而可能造成信息丢失。
更可靠的去重过程通常包括四步。
第一步:识别重复组
先根据业务唯一键找出重复对象:
第二步:确定主记录
主记录可以按照以下规则选择:
业务编码最早创建;
更新时间最新;
字段完整度最高;
历史交易记录最多;
已通过业务认证;
被下游系统引用最多。
企业最好把多项条件组合成优先级,而不是只依赖一个字段。
第三步:合并有效信息
重复记录中的信息可能需要互补,而不是全部舍弃。
例如,可以保留主记录的客户编号,同时补充其他记录中的地址、联系人和客户等级。对于冲突字段,则需要按照数据来源可信度、更新时间或者业务确认结果处理。
第四步:处理关联关系
删除重复客户前,必须检查订单、合同、开票和回款表是否引用旧客户编号。
通常需要先建立映射表:
再把下游业务表中的旧编号替换为主编号,最后才处理重复记录。
去重真正解决的不是“表中多了几行”,而是同一业务对象在企业内部存在多个身份的问题。
如果只在下游反复执行去重,却不增加唯一索引、录入校验和主数据规则,重复数据仍会持续产生。
三、空值处理:NULL、空字符串、0和“未知”不能混在一起
数据表中的空值,通常不止一种形式:
NULL空字符串
''一个或多个空格
“暂无”
“未知”
“-”
“N/A”
数字字段中的0
日期字段中的特殊默认值
这些值看起来都表示“没有数据”,但业务含义可能完全不同。
数据库中的NULL一般表示未知或缺失;空字符串表示字段存在但未填写;0则是一个明确的数值。
例如:
计算平均值时,AVG()通常忽略NULL,但会把0纳入计算。
如果订单金额暂时没有录入,却被统一填成0,平均订单金额就会被人为拉低;如果未付款金额被填成0,则可能被误判为客户已经结清。
因此,处理空值时,首先要区分空值产生的原因。
1、未采集
业务人员没有填写,例如客户邮箱为空。
2、暂时未知
目前没有结果,但未来会补充,例如订单尚未确定发货日期。
3、业务不适用
字段对当前记录没有意义,例如线下客户没有线上账号。
4、系统处理失败
数据同步、字段解析或格式转换失败,导致目标字段为空。
这四类空值的处理方式不应该相同。
首先,可以把不同形式的“伪空值”统一转换为NULL:
需要注意,生产环境中通常更建议在清洗层生成新字段或新表,而不是直接更新源表。
对于允许使用默认值的字段,可以使用COALESCE:
但默认值必须有明确业务依据。
折扣金额为空,可以在规则确认后按0处理;付款日期为空,却通常表示尚未付款;客户等级为空,也不能直接认定为普通客户。
更稳妥的方法,是保留原字段,同时增加状态字段:
对于必填字段,还可以统计空值率:
空值率不仅用于一次清洗,也可以作为长期数据质量指标。
例如,客户手机号空值率从2%突然上升到20%,问题可能不是客户突然不愿提供手机号,而是录入页面、接口映射或者同步任务发生了变化。
如果多张表都需要执行空格清除、空值替换、格式转换和缺失检测,可以将这些逻辑放入FineDataLink的数据处理流程中统一管理。规则发生变化时,只需调整对应节点或脚本,不必在不同报表和数据库任务中逐个修改。
四、异常值处理:先判断违反了什么规则,再决定是否修复
异常值是最容易被误删的一类数据。
订单金额突然达到1000万元,可能是录入人员多写了两个0,也可能是真实的大客户订单;某商品销量突然增长5倍,可能是数据重复,也可能是促销活动产生的真实增长。
所以,异常值表示偏离常态,但不一定表示数据错误。
实际处理中,可以从四个层面识别异常。
1、取值范围异常
某些字段存在明确边界,例如:
折扣率应在0到1之间;
数量原则上不能小于0;
年龄不能为负数;
已完成订单金额不能为0;
日期不能超出合理业务周期。
范围规则适合识别明确错误,但边界要结合业务定义。例如,库存数量出现负数,可能代表超卖,也可能是系统允许的负库存。
2、字段关系异常
单个字段看起来正常,多个字段组合后却不符合业务逻辑。
例如:
发货日期早于下单日期;
回款金额大于应收金额;
已取消订单仍有发货数量;
订单状态为“已完成”,但完成时间为空。
字段关系规则通常比单字段范围判断更有价值,因为它更接近真实业务流程。
3、参照完整性异常
业务表中的编码,应当能够关联到有效主数据。
例如,订单中的客户编号必须存在于客户表,商品编号必须存在于商品表。
出现无法关联的记录,可能是主数据缺失、编码变化、接口截断或者同步顺序错误。
这类问题不能只在订单表中修正,还要继续追溯上游系统。
4、统计分布异常
对于订单金额、交付周、库存周转天数等连续变量,可以使用均值、标准差或分位数识别极端值。
例如,使用四分位距判断:
统计方法只能筛选“值得关注的记录”,不能直接作为删除依据。
更合理的处理方式,是为数据增加异常类型和处理状态:
异常记录可以分为三类:
可以自动修复:例如清除空格、统一日期格式;
可以按明确规则处理:例如测试订单不计入正式统计;
必须人工确认:例如超大金额订单、客户主体冲突。
清洗任务中还可以把未通过规则的数据单独写入异常表,保留原始值、异常原因、发现时间和处理状态。
围绕这一过程,FineDataLink不只是执行SQL,还可以把异常识别、正常数据输出、异常数据分流和定时运行连接起来。这样,正常记录继续进入数据仓库,问题记录进入待核查区域,避免少量异常数据阻塞整批任务,也避免异常被无声过滤。
结语
SQL数据清洗看似是在处理重复值、空值和异常值,实际解决的是三个更深层的问题:
同一个业务对象怎样保持唯一,缺失数据怎样保留真实含义,异常数据怎样在错误和业务信号之间作出判断。
因此,可靠的数据清洗不能只追求“执行成功”,还要做到:
原始数据能够保留;
清洗规则有业务依据;
处理结果能够验证;
异常记录可以追溯;
清洗逻辑能够重复运行;
上游问题能够定位到具体系统和责任环节。
真正高质量的数据,不是表面上没有空值和重复值,而是每一次修改都有依据,每一条异常都有去向,每一个指标都能追溯到可信的数据来源。