查看重复数据数量(以id为重复列)
select id from (select id from laundry.cr01_archive group by id having count(*) > 1) as duplicates;
一、重删前的检查数据
删除前对数据表量进行统计(方便后续进行验证数据一致性),所有数据重删前都要进行检查。
1、静态表适用
(1)查看当前数据(生产中不用看)
mysql> select * from test01 order by id; +----+-------+------------------+ | id | name | valid_card_number | +----+-------+------------------+ | 1 | LLL01 | 111 | | 1 | LLL01 | 111 | | 1 | LLL01 | 111 | | 1 | LLL01 | 111 | | 1 | LLL01 | 111 | | 2 | LLL02 | 111 | | 2 | LLL02 | 111 | | 2 | LLL02 | 111 | | 2 | LLL02 | 111 | | 3 | LLL03 | 111 | | 3 | LLL03 | 111 | | 4 | LLL04 | 111 | | 4 | LLL04 | 111 | | 5 | LLL05 | 111 | | 5 | LLL05 | 111 | | 6 | LLL06 | 111 | | 6 | LLL06 | 111 | | 7 | LLL07 | 111 | +----+-------+------------------+
(2)获取表当前的数据总量
select count(*) from test01; --18条
(3)查看以ID为重复值的列,共有多少组有重复值
select id from test01 group by id having count(*) > 1; --6组
select count(id) from (select id from test01 group by id having count(*) > 1) as duplicates;--6组
(4)查看去重后有多少条
select count(*) from (select distinct(id) from test01) as distincts; --7条
(5)需要删除的数据有多少条?
18-7=11条
2、动态表适用(数据量不断变化?)
确认数据时间点,比如确认数据的时间范围条件,确保这短时间的数据是稳定的。
测试可以通过,不间断的插入数据,作为测试条件。
写一个mysql数据库存储过程,不间断的插入数据。包括时间点
(1)先插入批量数据,比如200行,
(2)确认一个时间点,然后查询这个时间点的数据
注意:如果这个时间点又有了重复数据,怎么办?
不会的,因为我们给表创建了一个新的自增列(自增列拥有唯一性),所以不可能再出现重复数据的情况。
因为自增列最后要删除的,如果最后删除后,又有了新的重复列(比如前两列一样,第三列是自增列),删除第三列,前两列还是有重复的,那么此时再走一遍去重操作?重复数据少的情况下,其实很快,上亿的数据,也就几分钟就可以去重,但增加自增列可能需要一些时间,只能祈祷在这个时间里,不要再有除了自增列再有重复数据的情况。
需要测试,测试,测试。
二、使用存储过程删除
1、方式1--存储过程直接循环删除重复数据
缺点:此存储过程执行一次只删除每组重复行的一条数据行,如果相同的重复行有几十次,几百次,就不好用了
(1)创建存储过程
DELIMITER $$ CREATE PROCEDURE RemoveDuplicateRecords_cbbt_coin_collection() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE current_id INT; DECLARE cur CURSOR FOR SELECT id FROM ( SELECT id, ROW_NUMBER() OVER(PARTITION BY id ORDER BY id) AS row_num FROM laundry.cbbt_coin_collection ) AS duplicates WHERE row_num > 1; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN cur; read_loop: LOOP FETCH cur INTO current_id; IF done THEN LEAVE read_loop; END IF; DELETE FROM laundry.cbbt_coin_collection WHERE id = current_id LIMIT 1; END LOOP; CLOSE cur; END$$ DELIMITER ;
(2)执行存储过程—移除重复复数据
CALL RemoveDuplicateRecords();
2、方式2--存储过程利用临时表方式删除重复数据(未整理完全,未通过测试)
为了快速删除重复数据,可以编写一个存储过程来处理这个任务。存储过程将会使用循环和临时表来识别和删除重复数据。以下是一个示例存储过程的框架,你可以根据具体情况进行调整和优化:
(1)创建存储过程
DELIMITER // CREATE PROCEDURE deleteDuplicateRows() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE id_value INT; DECLARE min_t_id INT; -- Cursor to iterate over distinct id values DECLARE cur CURSOR FOR SELECT DISTINCT id FROM your_table; -- Temporary table to store min t_id for each id CREATE TEMPORARY TABLE IF NOT EXISTS tmp_min_t_id ( id INT, min_t_id INT, PRIMARY KEY (id) ); -- Handler for cursor DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -- Open cursor and iterate over each id OPEN cur; read_loop: LOOP FETCH cur INTO id_value; IF done THEN LEAVE read_loop; END IF; -- Find minimum t_id for current id SELECT MIN(t_id) INTO min_t_id FROM your_table WHERE id = id_value; -- Insert into temporary table INSERT INTO tmp_min_t_id (id, min_t_id) VALUES (id_value, min_t_id); END LOOP; CLOSE cur; -- Delete duplicate rows except for the ones with min t_id DELETE yt FROM your_table yt JOIN tmp_min_t_id tmp ON yt.id = tmp.id AND yt.t_id <> tmp.min_t_id; -- Drop temporary table DROP TEMPORARY TABLE IF EXISTS tmp_min_t_id; END // DELIMITER ;
说明:
- Cursor 部分:使用游标遍历表中的每个不同的
id
值。 - 临时表:创建临时表
tmp_min_t_id
,用于存储每个id
对应的最小t_id
值。 - 循环处理:在循环中,对每个
id
找到其最小t_id
值,并插入临时表。 - 删除操作:利用临时表,删除表
your_table
中除了每个id
对应的最小t_id
之外的所有记录。 - 清理工作:删除完成后,临时表会自动删除。
使用方法:
三、使用SQL语句进行删除
1、使用sql语句删除--新增主键列方式
(此方式适用于表数据量不多,比如几百万行,但是重复数据较多,比如几十万行的)
(1) 第一步 新增自增主键(此步骤是为了辅助第二步)
运行前提是表中没有自增键,如果自增键,则无需本步,
ALTER TABLE test01 ADD COLUMN t_id INT AUTO_INCREMENT PRIMARY KEY FIRST;
(2)第二步-删除重复数据
DELETE t1 FROM test01 t1 JOIN ( SELECT id, MIN(t_id) AS keep_id FROM test01 GROUP BY id HAVING COUNT(*) > 1 ) t2 ON t1.id = t2.id AND t1.t_id <> t2.keep_id;
DELETE n1
FROM n02_archive n1
JOIN (
SELECT MIN(t_id) AS min_id, trans_id
FROM n02_archive GROUP BY trans_id
) n2 ON n1.t_id <> n2.min_id AND n1.trans_id = n2.trans_id;
DELETE t1
FROM test01 t1
JOIN (
SELECT id, MIN(t_id) AS keep_id
FROM test01
GROUP BY id
HAVING COUNT(*) > 1
) t2 ON t1.id = t2.id AND t1.t_id <> t2.keep_id;
(3)第三步 删除 t_id 列
ALTER TABLE test01 DROP COLUMN t_id;
(4)第四步-添加主键
ALTER TABLE test01 ADD PRIMARY KEY (trans_id);
如果你的 MySQL 表没有主键且没有任何唯一属性列,但你需要快速创建主键并保留每组重复数据中的第一条记录,可以按照以下步骤操作:
2、快速创建主键的方法(和1一样)
(1)添加自增列作为临时主键:
首先,为表添加一个新的自增列作为临时主键。标识和删除重复记录,同时为表提供一个唯一标识。
ALTER TABLE your_table ADD COLUMN temp_id INT AUTO_INCREMENT PRIMARY KEY;
上述命令会在 your_table 表中添加一个名为 temp_id 的新列,自动为每行分配唯一的整数值作为主键。
(2)使用临时主键删除重复记录:
接下来,使用临时主键来标识并删除重复记录。这里假设你的表有重复数据,但你希望保留每组重复数据中的第一条记录。
DELETE t1 FROM your_table t1 JOIN ( SELECT MIN(temp_id) AS keep_id FROM your_table GROUP BY id HAVING COUNT(*) > 1 ) t2 ON t1.temp_id <> t2.keep_id; your_table 是你的表名。 temp_id 是刚刚添加的临时主键列。 查询会找到每组重复数据中的第一条记录(具有最小的 temp_id 值),并删除除了第一条记录外的所有其他记录。
(3)移除临时主键并添加真正的主键:
删除完重复记录后,你可以移除临时主键列,并添加真正的主键。如果你有其他唯一属性列,可以选择将其设置为主键;否则,可以继续使用自增列作为主键。
ALTER TABLE your_table DROP COLUMN temp_id,ADD PRIMARY KEY (auto_increment_column); -- 这里替换为真正的主键列
--auto_increment_column 是你选择作为主键的列名,可以是刚刚添加的 temp_id 或其他合适的列。
注意事项:
- 在执行这些操作之前,请务必备份你的数据,以防止意外情况。
- 临时主键列只是为了标识和处理重复记录,最终的主键应该是表中的一个唯一标识。
- 确保在删除重复记录后检查数据是否符合预期,特别是每组重复数据是否只保留了第一条记录。
四、python脚本去重
import mysql.connector # 连接数据库,此处按需更改数据库信息 mysqlConnection = mysql.connector.connect( host="host", port=000, user="user", password="password", database="database" ) # 创建游标 mysqlCursor = mysqlConnection.cursor() # 待执行的 SQL 列表 sql_queries = [ """ delete from user_third_login where id in (select id from (select id from user_third_login group by id having count(*) > 1) as duplicates) order by id limit 1;""", """ delete from user_refered_friend where id in (select id from (select id from user_refered_friend group by id having count(*) > 1) as duplicates) order by id limit 1;""", """ delete from user_third_login_deleted where id in (select id from (select id from user_third_login_deleted group by id having count(*) > 1) as duplicates) order by id limit 1;""" ] # 循环执行 SQL for query in sql_queries: while True: # 执行 SQL mysqlCursor.execute(query) # 获取并打印数据库返回的结果 deleted_rows = mysqlCursor.rowcount if deleted_rows == 0: print("This sql returns: Affected rows: 0", query.strip()) break else: print("Deleted", deleted_rows, "duplicate entries for:", query.strip()) # 提交事务 mysqlConnection.commit() # 关闭游标和数据库连接 mysqlCursor.close() mysqlConnection.close()