查看重复数据数量(以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()