重复数据分类:完全重复与部分重复,精确匹配与模糊匹配,单字段与多字段重复

做数据去重,第一件事不是写代码,而是搞清楚你面对的是哪种「重复」。

我见过不少团队,上来就怼一个 DISTINCT 或者 GROUP BY,结果数据一跑,该去的没去掉,不该删的删了一大片。说白了,重复数据不是铁板一块,它有好几种面孔。你认不出它,它就坑你。

这一节,咱们就把重复数据的分类掰开揉碎讲清楚。我按三个维度来切:重复程度、匹配方式、字段范围。你把这套分类体系吃透了,后面写去重逻辑的时候,心里就有谱了。

核心观点:重复数据分类是去重策略的「地图」。没有这张地图,你就是在盲人摸象。

重复数据分类知识体系 重复数据分类 按重复程度 按匹配方式 按字段范围 完全重复 所有字段一致 部分重复 关键字段一致 精确匹配 值完全相等 模糊匹配 相似度阈值 单字段重复 一个字段判断 多字段重复 组合字段判断 场景:ETL 全量快照 场景:用户信息合并 场景:订单去重

一、按重复程度:完全重复 vs 部分重复

这是最直观的分类方式。我刚开始做数据清洗的时候,以为重复就是「两行数据一模一样」。后来发现,事情远没那么简单。

1. 完全重复(Exact Duplicate)

两行记录的所有字段值都完全相同。这种重复最常见,也最好处理。

举个例子:

user_id name email phone
1001 张三 zhangsan@example.com 13800138001
1001 张三 zhangsan@example.com 13800138001

这两行完全一样。原因可能是上游系统重复推送、接口重试、或者人为误操作。处理方式很简单:DISTINCT 或者 ROW_NUMBER() 去重就行。

我的经验:完全重复往往不是业务逻辑问题,而是数据管道的问题。我曾经在一个日志系统里发现,某台服务器因为网络抖动,把同一批数据发了三遍。去重只是治标,找到根因才是治本。

2. 部分重复(Partial Duplicate)

两行记录只有部分关键字段相同,其他字段可能不同。这才是真正考验功底的地方。

举个例子:

order_id user_id product amount create_time
ORD001 1001 手机 2999 2024-01-01 10:00:00
ORD001 1001 手机 2999 2024-01-01 10:00:05

你看,order_id、user_id、product、amount 都一样,但 create_time 差了 5 秒。这算不算重复?

算。因为业务上,同一个订单不可能在 5 秒内下两次。这多半是系统并发处理时产生的时间戳偏差。

注意:部分重复的判断标准取决于业务规则。同一个用户同一天下了两笔相同金额的订单,在电商系统里可能是正常行为,但在风控系统里可能就是重复。一定要跟业务方确认「重复」的定义。

二、按匹配方式:精确匹配 vs 模糊匹配

这个维度决定了你怎么「找」重复。说白了,就是比较两个值的时候,用等号还是用相似度。

1. 精确匹配(Exact Match)

值必须完全相等。比如 '张三' == '张三',1001 == 1001。这种匹配效率高,适合主键、ID、标准化后的字段。

我建议,能用精确匹配的地方,绝不用模糊匹配。为什么?因为精确匹配可以用哈希、索引、排序,性能好得多。

-- 精确匹配去重示例(SQL)
SELECT DISTINCT user_id, email
FROM users;

-- 或者用窗口函数保留第一条
WITH ranked AS (
  SELECT *,
         ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at) AS rn
  FROM users
)
SELECT * FROM ranked WHERE rn = 1;

2. 模糊匹配(Fuzzy Match)

值不完全相等,但「看起来很像」。比如 '张 三' 和 '张三',138-0013-8001 和 13800138001。

模糊匹配常用的方法有:

  • 编辑距离(Levenshtein Distance):计算两个字符串需要多少次增删改才能变成一样
  • Jaccard 相似度:计算两个集合的交集与并集之比
  • Soundex / Metaphone:按发音匹配,适合人名
  • 正则表达式归一化:先清洗再精确匹配

避坑指南:我曾经在一个客户数据合并项目里,直接用编辑距离匹配姓名,结果把「王芳」和「王方」这种真·不同的人给合并了。后来加了「地区+生日」作为辅助判断,才把误杀率降下来。模糊匹配一定要设阈值,而且阈值要经过测试。

三、按字段范围:单字段 vs 多字段

这个维度决定了「重复」的判断依据是什么。是一个字段说了算,还是多个字段一起说了算。

1. 单字段重复(Single-Field Duplicate)

只根据一个字段的值来判断是否重复。比如「邮箱不能重复」、「身份证号不能重复」。

这种场景最简单,直接 GROUP BY 或者建唯一索引就行。

-- 单字段重复检测
SELECT email, COUNT(*)
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

2. 多字段重复(Multi-Field Duplicate)

需要多个字段组合起来判断。比如「同一个用户在同一天不能下两笔相同金额的订单」。

多字段重复又分两种:

  • 组合键重复:多个字段的值组合起来必须唯一。比如 (user_id, order_date, amount)
  • 业务规则重复:字段之间有关系,比如「地址相似 + 姓名相似 + 电话相同」才算重复
-- 多字段组合重复检测
SELECT user_id, DATE(create_time), amount, COUNT(*)
FROM orders
GROUP BY user_id, DATE(create_time), amount
HAVING COUNT(*) > 1;

我的习惯:多字段去重时,我会先建一个「去重指纹」字段。比如把 user_id、date、amount 拼接起来,做一次 MD5。这样后续判断就变成了单字段精确匹配,性能提升很明显。

四、分类组合:实际场景中的交叉应用

这三个维度不是孤立的。实际项目中,它们经常组合出现。我整理了一张对照表,方便你快速定位:

场景 重复程度 匹配方式 字段范围 典型处理
ETL 全量快照 完全重复 精确匹配 全字段 DISTINCT / 去重表
用户注册 部分重复 精确匹配 单字段(邮箱) 唯一索引
客户数据合并 部分重复 模糊匹配 多字段(姓名+电话+地址) 相似度计算 + 人工审核
订单去重 部分重复 精确匹配 多字段(订单号+金额) 窗口函数 + 时间窗口
日志去重 完全重复 精确匹配 全字段 Bloom Filter / 哈希

你看,同一个场景可能同时涉及多个分类维度。比如「客户数据合并」就是「部分重复 + 模糊匹配 + 多字段」的组合,这种最复杂,也最容易踩坑。

再次提醒:分类不是目的,目的是选对去重策略。你花 10 分钟把重复数据的「画像」画清楚,后面写代码可能只需要 5 分钟。反过来,上来就写代码,调试可能要花 2 小时。