Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL处理重复数据

MySQL处理重复数据完整代码实例

作者:kushu7

在数据库管理中,查找重复值是一项常见需求,下面这篇文章主要介绍了MySQL处理重复数据的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

在数据库管理中,重复数据是最常见的 “隐形杀手” 之一。它不仅会浪费存储空间、拖慢查询速度,还可能导致业务逻辑混乱(如统计结果失真、订单重复处理)。

一、认识重复数据:从 “什么是重复” 开始

重复数据并非仅指完全相同的记录,在实际业务中可分为两类:

二、检测重复数据:

1. 检测完全重复记录

-- 查找完全重复的记录
SELECT * FROM table_name
WHERE (col1, col2, ..., coln) IN (
SELECT col1, col2, ..., coln
FROM table_name
GROUP BY col1, col2, ..., coln
HAVING COUNT(*) > 1
);

适用于所有字段均需唯一的场景(如配置表、字典表):

示例:检测user_config表中完全重复的配置记录:

SELECT * FROM user_config
WHERE (user_id, config_key, config_value) IN (
SELECT user_id, config_key, config_value
FROM user_config
GROUP BY user_id, config_key, config_value
HAVING COUNT(*) > 1
);

2. 检测部分重复记录(按核心字段)

适用于仅需保证核心字段唯一的场景(如用户表的手机号、订单表的订单号):

-- 按核心字段分组,查找重复记录
SELECT core_col1, core_col2, COUNT(*) AS duplicate_count
FROM table_name
GROUP BY core_col1, core_col2
HAVING COUNT(*) > 1;

示例:检测users表中重复的手机号(核心字段为phone):

-- 查看重复手机号及重复次数
SELECT phone, COUNT(*) AS duplicate_count
FROM users
GROUP BY phone
HAVING COUNT(*) > 1;
-- 查看重复手机号对应的完整记录
SELECT * FROM users
WHERE phone IN (
SELECT phone
FROM users
GROUP BY phone
HAVING COUNT(*) > 1
)
ORDER BY phone;

3. 高级检测:带条件的重复记录

结合业务逻辑筛选重复记录(如重复且状态有效的订单):

-- 查找状态为"已支付"的重复订单
SELECT order_no, COUNT(*) AS duplicate_count
FROM orders
WHERE status = 'PAID'
GROUP BY order_no
HAVING COUNT(*) > 1;

4. 使用窗口函数标记重复记录(MySQL 8.0+)

通过ROW_NUMBER()为重复记录编号,便于后续处理:

-- 为重复手机号的记录编号(按注册时间排序)
SELECT
id, phone, register_time,
ROW_NUMBER() OVER (PARTITION BY phone ORDER BY register_time) AS rn
FROM users;

三、删除重复数据:保留有效记录

删除重复数据的核心原则是:保留一条有效记录(如最新 / 最早的记录),删除其余重复项。以下是 4 种实用方法:

1. 带唯一标识的重复记录删除(推荐)

若表中有自增主键(如id),可通过子查询定位并删除重复记录:

-- 保留重复手机号中id最小的记录(即最早插入的记录)
DELETE FROM users
WHERE id NOT IN (
SELECT min_id FROM (
-- 子查询嵌套避免"不能从同表查询并删除"的限制
SELECT MIN(id) AS min_id
FROM users
GROUP BY phone
HAVING COUNT(*) > 1
) AS temp
);

逻辑解析

  1. 内层子查询找出每组重复记录中的最小id(要保留的记录)。
  1. 外层删除所有id不在保留列表中的记录。

2. 无唯一标识的重复记录删除

若表无主键,可通过所有字段组合定位重复记录:

-- 保留完全重复记录中一条(需指定所有字段)
DELETE t1 FROM table_name t1
JOIN table_name t2
ON t1.col1 = t2.col1
AND t1.col2 = t2.col2
AND ...
AND t1.coln = t2.coln
WHERE t1.ctid < t2.ctid; -- 利用隐藏列ctid区分物理位置(仅InnoDB有效)

3. 按条件保留记录(如最新记录)

通过排序保留指定条件的记录(如最新注册的用户):

-- 保留重复手机号中注册时间最新的记录
DELETE t1 FROM users t1
JOIN users t2
ON t1.phone = t2.phone
AND t1.register_time < t2.register_time; -- t1为旧记录

4. 批量删除大表重复数据(性能优化)

当表数据量超过 100 万行时,直接删除可能导致锁表,建议分批次处理:

-- 每次删除1000条重复记录(循环执行至无重复)
DELETE FROM users
WHERE id IN (
SELECT id FROM (
SELECT id
FROM (
SELECT
id,
ROW_NUMBER() OVER (PARTITION BY phone ORDER BY register_time) AS rn
FROM users
) AS t
WHERE rn > 1
LIMIT 1000 -- 限制单次删除数量
) AS temp
);

注意事项:

四、预防重复数据:从源头阻断

处理重复数据的最佳方式是提前预防,以下是 3 种核心手段:

1. 建立唯一约束(最有效)

通过唯一索引主键强制核心字段唯一:

-- 为手机号添加唯一索引,阻止重复插入
CREATE UNIQUE INDEX uk_users_phone ON users(phone);
-- 复合唯一索引(如同一用户的配置键唯一)
CREATE UNIQUE INDEX uk_user_config ON user_config(user_id, config_key);

2. 插入时处理重复数据

通过INSERT ... IGNORE或REPLACE INTO在插入阶段处理重复:

-- 插入时忽略重复记录(不报错,返回警告)
INSERT IGNORE INTO users (phone, name)
VALUES ('13800138000', '张三');
-- 重复时替换旧记录(删除旧记录后插入新记录)
REPLACE INTO users (phone, name)
VALUES ('13800138000', '张三');

3. 业务层控制

在应用程序中添加重复校验逻辑:

// Java示例:插入前检查手机号是否已存在
public boolean addUser(User user) {
// 先查询是否存在重复手机号
if (userDao.existsByPhone(user.getPhone())) {
throw new DuplicateException("手机号已注册");
}
return userDao.insert(user) > 0;
}

总结

到此这篇关于MySQL处理重复数据的文章就介绍到这了,更多相关MySQL处理重复数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文