不管是程序BUG,还是业务变更,重复数据这个老生常谈的问题,总是会出现。以下是我在MariaDB或是MySQL下处理的一些经验。在SQL Server中,使用窗口函数是很容易实现的。不过听说MySQL 8.0和MariaDB 10.2以上均支持窗口函数了。等有机会再来测试使用窗口函数来删除重复记录。

背景

表t_record中的数据fromUserId, toUserId两个字段组合作为唯一的标识,删除重复记录,只留下最大id(或最新时间)的记录。id为自增无重复的主键。

表t_record的id作为自增的主键。

表t_record大概有6万多的数据。以下测试均在资源很差的主机上,t_record没有在使用的情况下的结果。

方法1

查询重复的记录

SELECT fromUserId, toUserId, count(*)
FROM t_record as tr
GROUP BY fromUserId, toUserId
HAVING count(*) > 1;

把重复记录的两个字段放到临时表_tmp1中

CREATE TABLE _tmp1
SELECT fromUserId, toUserId
FROM t_record as tr
GROUP BY fromUserId, toUserId
HAVING count(*) > 1;

把应该删除的id查询出来,放到临时表_tmp2中

CREATE TABLE _tmp2
SELECT id
FROM t_record as a
WHERE (a.fromUserId, a.toUserId) in ( SELECT fromUserId, toUserId from _tmp1
)
and a.id not in (
SELECT MAX(id)
FROM t_record as tr
GROUP BY fromUserId, toUserId
HAVING count(*) > 1
);

删除原表的记录

DELETE from t_record
where id in (
SELECT id from _tmp2
);

通过以前3个步骤,没有删除数据大概需要23秒左右。

方法2

如果表中没有主键,也没有可以标识唯一记录的字段。只能是把原表的数据分表后,插入到另一张临时表,删除原表数据,把临时表的数据导回来。

这种方法也适用合于有主键或有唯一标识的表,但操作过程中会影响在线的业务,需要中断业务。否则可能会造成数据丢失或数据不一致。

数据量大的表,导两次数据,过程会很慢,同时也需要注意硬盘空间是否足够。

方法3

测试mysql不支持以下这种delete语法来删除数据。改为select id 存到临时表,查询非常慢。

DELETE a
FROM table_nam a
WHERE EXISTS (SELECT 1 FROM table_nam b
WHERE b.userid = a.userid AND b.CreateDate > a.CreateDate);

方法4

在mariadb 10.1.19下测试,60多秒。

这个方法简单,只需要一条语句,速度还行。

DELETE
from t_record
where id not in (
select maxid from
(select max(id) as maxid from t_record
group by fromUserId,toUserId
) b
);

现对方法4的进行改造,再测试下。11.5秒。快好几倍了。

CREATE OR REPLACE TABLE _tmp3
SELECT id
FROM t_record
WHERE id NOT IN (
SELECT maxid FROM
(SELECT max(id) AS maxid FROM t_record
GROUP BY fromUserId,toUserId
) b
); DELETE FROM t_record
WHERE id IN (
SELECT id from _tmp3
);

对临时表创建主键,再测试下。1.2秒!WOW!!!

CREATE OR REPLACE TABLE _tmp3 (id INT NOT NULL PRIMARY KEY);

INSERT INTO _tmp3 (id)
SELECT id
FROM t_record
WHERE id NOT IN (
SELECT maxid FROM
(SELECT max(id) AS maxid FROM t_record
GROUP BY fromUserId,toUserId
) b
); DELETE FROM t_record
WHERE id IN (
SELECT id from _tmp3
);

还以不能再快呢?改IN子句为JOIN,再测试下。1秒!

CREATE OR REPLACE TABLE _tmp3 (id INT NOT NULL PRIMARY KEY);

INSERT INTO _tmp3 (id)
SELECT id
FROM t_record
WHERE id NOT IN (
SELECT maxid FROM
(SELECT max(id) AS maxid FROM t_record
GROUP BY fromUserId,toUserId
) b
); DELETE a FROM t_record as a INNER JOIN _tmp3 as b on b.id = a.id;

难道删除的那个语句的执行计划是不同的吗?由于mysql只支持select的执行查询,所以把删除的语句修改为查询语句。

root@localhost [db1]EXPLAIN SELECT id FROM t_record WHERE id IN ( SELECT id from _tmp3 );
+------+-------------+-------------------+--------+---------------+---------+---------+--------------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------------------+--------+---------------+---------+---------+--------------------+------+--------------------------+
| 1 | PRIMARY | _tmp3 | index | PRIMARY | PRIMARY | 4 | NULL | 452 | Using index |
| 1 | PRIMARY | t_record | eq_ref | PRIMARY | PRIMARY | 8 | testdb._tmp3.id | 1 | Using where; Using index |
+------+-------------+-------------------+--------+---------------+---------+---------+--------------------+------+--------------------------+
2 rows in set (0.00 sec)
root@localhost [db1]EXPLAIN SELECT a.id FROM t_record as a INNER JOIN _tmp3 as b on b.id = a.id;
+------+-------------+-------+--------+---------------+---------+---------+----------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+---------------+---------+---------+----------------+------+--------------------------+
| 1 | SIMPLE | b | index | PRIMARY | PRIMARY | 4 | NULL | 452 | Using index |
| 1 | SIMPLE | a | eq_ref | PRIMARY | PRIMARY | 8 | testdb.b.id | 1 | Using where; Using index |
+------+-------------+-------+--------+---------------+---------+---------+----------------+------+--------------------------+
2 rows in set (0.01 sec)

对比执行计划,是一样的。但测试多次,用JOIN方式速度还是快些。为什么呢?






MariaDB删除重复记录的更多相关文章

  1. MariaDB删除重复记录性能测试

    删除重复记录,只保留id最大的一条记录的性能测试 环境 测试表的id为是唯一的,或是自增的主键. mysql不能直接写循环,只能写在存储过程里. 存储过程usp_batch_insert的参数num_ ...

  2. mysql删除重复记录语句的方法

    例如: id name value 1 a pp 2 a pp 3 b iii 4 b pp 5 b pp 6 c pp 7 c pp 8 c iii id是主键 要求得到这样的结果 id name ...

  3. mysql 删除重复记录语句

    mysql 根据条件删除重复记录 只保留最小id的重复数据 DELETEFROM newsWHERE news_id IN ( SELECT a.news_id FROM ( SELECT news_ ...

  4. sql查询重复记录、删除重复记录方法大全

    查找所有重复标题的记录:SELECT *FROM t_info aWHERE ((SELECT COUNT(*)FROM t_infoWHERE Title = a.Title) > 1)ORD ...

  5. mysql 数据表中查找、删除重复记录

    为了性能考虑,在阅读之前提醒大家,如果有子查询,子查询查询到的数据最好不要超过总数据量的30%. 查询有重复数据的记录 select * from F group by a,b,c,d having ...

  6. [SQL]查询及删除重复记录的SQL语句

    一:查询及删除重复记录的SQL语句1.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断select * from peoplewhere peopleId in (select ...

  7. MySQL查询及删除重复记录的方法

    查询及删除重复记录的方法(一)1.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断select * from peoplewhere peopleId in (select p ...

  8. Oracle 查询并删除重复记录的SQL语句

    查询及删除重复记录的SQL语句 1.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断select * from peoplewhere peopleId in (select  ...

  9. mysql插入数据与删除重复记录的几个例子(收藏)

    mysql插入数据与删除重复记录的几个例子 12-26shell脚本实现mysql数据的批量插入 12-26mysql循环语句插入数据的例子 12-26mysql批量插入数据(insert into ...

随机推荐

  1. ThinkPHP 数据库操作之数据表模型和基础模型 ( Model )

    一.定义数据表模型 1.模型映射 要测试数据库是否正常连接,最直接的办法就是在当前控制器中实例化数据表,然后使用 dump 函数输出,查看数据库的链接状态.代码: public function te ...

  2. 【windows socket+HTTPserverclient】

    Windows Socket+HTTPserverclient      Winsock是 Windows下套接字标准.                 1.HTTP协议:          HTTP ...

  3. kafka分区原理图

    一个Topic的多个分区,被分布在kafka集群中的多个server上.每一个分区都有一个server为"leader";leader负责全部的读写操作,假设leader失效,那么 ...

  4. Windows 8 Metro 应用开发入门(一):开发环境介绍

    摘 要 Windows8已经发布,随之而来的基于WinRT的Metro应用也正向我们走来,正像它所宣传的:光滑.快.现代.看习惯了玻璃.立体风格的应用,或许Metro的简洁能给你留下不一样的体验.Vi ...

  5. Selenium2+python自动化48-登录方法(参数化)

    前言 登录这个场景在写用例的时候经常会有,我们可以把登录封装成一个方法,然后把账号和密码参数化,这样以后用的登录的时候,只需调用这个方法就行了 一.登录方法 1.把输入账号.输入密码.点击登录按钮三个 ...

  6. [MAC OS] XCode中的Debug View Hierarchy功能

    reference to : http://blog.csdn.net/liujinlongxa/article/details/46490949 前言 做iOS开发经常会遇见这种情况,产品汪拿着你做 ...

  7. 学习笔记:A*算法

    简易地图 如图所示简易地图, 其中绿色方块的是起点 (用 A 表示), 中间蓝色的是障碍物, 红色的方块 (用 B 表示) 是目的地. 为了可以用一个二维数组来表示地图, 我们将地图划分成一个个的小方 ...

  8. iOS:制作一个简易的计算器

    初步接触视图,制作了一个简易的计算器,基本上简单的计算是没有问题的,不是很完美,可能还有一些bug,再接再厉. // // ViewController.m // 计算器 // // Created ...

  9. 【STL学习】堆相关算法详解与C++编程实现(Heap)

    转自:https://blog.csdn.net/xiajun07061225/article/details/8553808 堆简介   堆并不是STL的组件,但是经常充当着底层实现结构.比如优先级 ...

  10. 《Linux总线、设备与驱动》USB设备发现机制

    说明:本分析基于mstar801平台Linux2.6.35.11内核,其他内核版本仅供参考. 一.程序在内核中的位置 1.usb host做为pci总线下的一个设备存在(嵌入式系统中有可能也会直接挂在 ...