在mysql数据库的日常使用中,重复数据的出现往往和业务流程设计、数据导入操作不规范等因素有关,掌握查询和处理重复数据的方法是开发者的必备技能。不同业务场景下的重复判定规则不同,对应的SQL语句写法也存在差异,下面分场景逐一讲解。

单字段重复数据的查询
最常见的重复场景是单个字段的值出现多次,比如用户表中手机号重复、订单表中订单号重复。查询这类重复数据的核心是使用GROUP BY分组统计,再筛选出出现次数大于1的分组。
查询重复的单字段值
假设有一张用户表user,其中phone字段是手机号,要查询所有重复的手机号,SQL语句如下:
-- 查询user表中重复的手机号 SELECT phone, COUNT(*) AS repeat_count FROM user GROUP BY phone HAVING COUNT(*) > 1;
上述语句中,GROUP BY phone会按照手机号分组,COUNT(*)统计每个分组的记录数,HAVING COUNT(*) > 1筛选出记录数大于1的分组,也就是重复的手机号及其出现次数。
查询重复数据对应的完整记录
很多时候我们不仅需要知道哪些值重复,还要拿到重复值对应的所有完整记录,这时候可以结合子查询实现:
-- 查询user表中所有手机号重复的用户完整记录
SELECT *
FROM user
WHERE phone IN (
SELECT phone
FROM user
GROUP BY phone
HAVING COUNT(*) > 1
)
ORDER BY phone;
多字段重复数据的查询
有些场景下重复数据的判定需要多个字段同时相同,比如订单表中同一个用户同一个商品的多条订单记录才算重复,这时候需要在GROUP BY中指定多个字段。
假设订单表order有user_id(用户ID)、goods_id(商品ID)、order_time(下单时间)字段,要查询同一个用户同一个商品的重复订单:
-- 查询多字段重复的订单记录 SELECT user_id, goods_id, COUNT(*) AS repeat_count FROM `order` GROUP BY user_id, goods_id HAVING COUNT(*) > 1;
如果需要查询这些重复订单的完整记录,同样可以用子查询实现:
-- 查询多字段重复的订单完整记录
SELECT *
FROM `order`
WHERE (user_id, goods_id) IN (
SELECT user_id, goods_id
FROM `order`
GROUP BY user_id, goods_id
HAVING COUNT(*) > 1
)
ORDER BY user_id, goods_id;
重复数据的处理:删除重复保留一条
查询到重复数据后,通常需要删除重复记录只保留一条有效数据,比如保留重复记录中ID最小的那条。
单字段重复数据去重
还是以用户表user为例,要删除重复的手机号记录,只保留每个手机号对应ID最小的那条:
-- 删除重复手机号记录,保留ID最小的一条
DELETE FROM user
WHERE id NOT IN (
SELECT min_id FROM (
SELECT MIN(id) AS min_id
FROM user
GROUP BY phone
) AS temp
);
这里需要注意,mysql不允许直接在子查询中查询要删除的表,所以需要再套一层子查询作为临时表temp,避免语法错误。
多字段重复数据去重
多字段重复的去重逻辑和单字段类似,只是分组时指定多个字段即可:
-- 删除多字段重复的订单记录,保留ID最小的一条
DELETE FROM `order`
WHERE id NOT IN (
SELECT min_id FROM (
SELECT MIN(id) AS min_id
FROM `order`
GROUP BY user_id, goods_id
) AS temp
);
注意事项
- 执行删除操作前,一定要先备份数据,或者先执行查询语句确认要删除的记录范围,避免误删有效数据。
- 如果表的数据量较大,
GROUP BY和子查询的操作可能会比较耗时,建议先在测试环境验证SQL语句的正确性。 - 如果重复字段上有索引,查询效率会明显提升,实际业务中可以根据重复判定的字段建立合适的联合索引。