admin管理员组文章数量:1794759
巧用 CTE 公共表达式删除 MySQL 重复数据
一段时间后,大多数应用程序可能由于bug而出现重复行,这不仅影响用户体验,还增加了存储需求并降低数据库性能。可以通过一个 SQL 查询来完成整个清理过程,从而有效解决这一问题。
使用范例
代码语言:sql复制-- 创建表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
-- 插入数据,包括重复数据
INSERT INTO users (name, email) VALUES
('Alice', 'alice@example'),
('Bob', 'bob@example'),
('Alice', 'alice@example'), -- 重复数据
('Charlie', 'charlie@example'),
('Bob', 'bob@example'); -- 重复数据
巧用 CTE 公共表达式删除 MySQL 重复数据
代码语言:sql复制WITH duplicates AS (
SELECT id, ROW_NUMBER() OVER(
PARTITION BY name, email
ORDER BY id DESC
) AS rownum
FROM users
)
DELETE users
FROM users
JOIN duplicates USING(id)
WHERE duplicates.rownum > 1;
输出结果:
代码语言:sql复制mysql> select * from users;
+----+---------+---------------------+
| id | name | email |
+----+---------+---------------------+
| 1 | Alice | alice@example |
| 2 | Bob | bob@example |
| 3 | Alice | alice@example |
| 4 | Charlie | charlie@example |
| 5 | Bob | bob@example |
+----+---------+---------------------+
5 rows in set (0.00 sec)
mysql> WITH duplicates AS (
-> SELECT id, ROW_NUMBER() OVER(
-> PARTITION BY name, email
-> ORDER BY id DESC
-> ) AS rownum
-> FROM users
-> )
-> DELETE users
-> FROM users
-> JOIN duplicates USING(id)
-> WHERE duplicates.rownum > 1;
Query OK, 2 rows affected (0.00 sec)
mysql> select * from users;
+----+---------+---------------------+
| id | name | email |
+----+---------+---------------------+
| 3 | Alice | alice@example |
| 4 | Charlie | charlie@example |
| 5 | Bob | bob@example |
+----+---------+---------------------+
3 rows in set (0.00 sec)
解读:
这个SQL语句可以分为两个主要部分
1)WITH子句(公共表表达式,CTE):
这部分创建了一个名为duplicates的临时结果集。它对users表进行操作:
- 使用PARTITION BY name, email对名字和邮箱相同的记录进行分组。
- 在每个分组内,使用ORDER BY id DESC按id降序排序。
- 为每条记录分配一个rownum,这个数字在每个分组内从1开始计数。
2)DELETE语句:
这部分执行实际的删除操作:
- 它将users表与我们刚刚创建的duplicates结果集进行JOIN。
- 删除条件是WHERE duplicates.rownum > 1,意味着它会删除每组重复记录中除了第一条(rownum = 1)之外的所有记录。
需要注意的是,这个DELETE语句的语法可能不被所有数据库系统支持。例如,在MySQL中这是有效的语法,但在其他一些数据库系统中可能需要稍作调整。
总的来说,这个查询的目的是:
- 找出users表中名字和邮箱相同的记录。
- 对于每组重复记录,保留id最大的那一条(因为是按id降序排序)。
- 删除其他所有重复记录。
这种方法可以有效地清理数据库中的重复用户记录,同时保留每组重复记录中最新的(假设id越大越新)一条记录。
补充:
当表很大的情况,需要进行分批次删除处理,这样会减缓主库IO,减缓主从复制延迟。
代码语言:sql复制DELIMITER //
CREATE PROCEDURE batch_delete_duplicates()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE batch_size INT DEFAULT 1000;
DECLARE sleep_interval INT DEFAULT 1;
DECLARE last_id INT DEFAULT 0;
-- 创建临时表来存储重复记录的ID
CREATE TEMPORARY TABLE IF NOT EXISTS temp_duplicates (
id INT PRIMARY KEY
);
-- 使用CTE找出所有重复记录,并插入临时表
INSERT INTO temp_duplicates (id)
WITH duplicates AS (
SELECT id, ROW_NUMBER() OVER(
PARTITION BY name, email
ORDER BY id DESC
) AS rownum
FROM users
)
SELECT id
FROM duplicates
WHERE rownum > 1;
-- 开始循环删除
WHILE NOT done DO
-- 删除一批重复记录
DELETE u FROM users u
INNER JOIN (
SELECT id
FROM temp_duplicates
WHERE id > last_id
ORDER BY id
LIMIT batch_size
) AS td ON u.id = td.id;
-- 更新last_id
SET last_id = (
SELECT IFNULL(MAX(id), last_id)
FROM temp_duplicates
WHERE id > last_id
ORDER BY id
LIMIT batch_size
);
-- 从临时表中删除已处理的记录
DELETE FROM temp_duplicates
WHERE id <= last_id;
-- 检查是否还有记录需要删除
IF (SELECT COUNT(*) FROM temp_duplicates) = 0 THEN
SET done = TRUE;
ELSE
-- 休眠指定的秒数
DO SLEEP(sleep_interval);
END IF;
END WHILE;
-- 删除临时表
DROP TEMPORARY TABLE IF EXISTS temp_duplicates;
END //
DELIMITER ;
CALL batch_delete_duplicates();
这个存储过程 batch_delete_duplicates()
用于批量删除表 users
中的重复数据。它会以每次删除1000行的方式进行,直到所有重复数据被删除完毕,每次删除完成后会休眠1秒。
解读:
1. 声明变量
done
: 用于判断是否已完成所有重复记录的删除,初始值为FALSE
。batch_size
: 每次删除的行数,默认值为1000。sleep_interval
: 每次删除操作后休眠的秒数,默认为1秒。last_id
: 用于跟踪上一次删除操作的最大id
,初始值为0。
2. 创建临时表
- 使用
CREATE TEMPORARY TABLE
创建了一个名为temp_duplicates
的临时表,用来存储users
表中重复记录的id
。
3. 查找并插入重复记录
- 使用 CTE (Common Table Expression)
duplicates
找出users
表中的重复记录。它根据name
和email
字段进行分组,并按照id
倒序排序。 - 然后,通过
ROW_NUMBER()
为每个分组内的行编号,保留rownum
值大于1的行,即将重复记录插入temp_duplicates
表。
4. 循环删除重复记录
- 使用
WHILE NOT done DO
循环结构,每次循环都删除一批batch_size
(即1000)条重复记录。- 通过
DELETE u FROM users u
与临时表temp_duplicates
连接,仅删除temp_duplicates
中的重复id
。 - 通过
SET last_id
语句更新last_id
的值,使其指向当前批次删除的最大id
。 - 从
temp_duplicates
表中删除已处理的记录,以避免重复处理。
- 通过
- 每次删除后,检查临时表
temp_duplicates
是否还有未处理的记录。如果没有剩余记录,将done
设置为TRUE
,退出循环。否则,程序会执行DO SLEEP(sleep_interval)
休眠1秒。
5. 删除临时表
- 在删除所有重复记录后,最后删除
temp_duplicates
临时表,清理数据库。
6. 调用存储过程
使用 CALL batch_delete_duplicates();
来调用并执行此存储过程。
总结
这个过程通过批次删除的方式来处理大量重复记录,以减少数据库的锁定时间并避免过高的资源消耗,同时通过休眠操作使得删除过程更加平稳。
本文标签: 巧用 CTE 公共表达式删除 MySQL 重复数据
版权声明:本文标题:巧用 CTE 公共表达式删除 MySQL 重复数据 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1754913697a1708111.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论