当表设计不规范或者应用程序的校验不够严谨时,就容易导致业务表产生重复数据。因此,学会高效地删除重复就显得尤为重要。
今天我们就来说怎么删除有主键的重复数据。提前预告:下一篇文章会介绍如何删除没有主键的重复数据。
可以只使用单条 SQL 语句删除表中的重复数据,也可以借助于临时表来达到这个目的。使用单条 SQL 语句的 好处是操作原子性的,不需要考虑事务;而借助于中间表的方式则需要分成多条 SQL 语句才能完成删除操作,这个过程需要启用事务来保持数据一致性。
在这里,我们只介绍使用单条语句的操作。操作步骤如下:
由于主键的存在,可以将重复数据中的对应的主键的最大或最小的那条记录标记为保留数据。
我们来看具体的操作,有一个 emp 表,emp 的表结构如下:
CREATE TABLE `emp` ( `empno` int(11) NOT NULL, `ename` varchar(10) DEFAULT NULL, `job` varchar(9) DEFAULT NULL, `mgr` int(11) DEFAULT NULL, `hiredate` date DEFAULT NULL, `sal` decimal(7,2) DEFAULT NULL, `comm` decimal(7,2) DEFAULT NULL, `deptno` int(11) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8
表中的数据如下图所示(标红的是重复数据):
字段 ename 作为判断重复数据的依据,即两条或者两条以上的数据的 ename 字段的值相同,说明它们都是重复数据。
当存在重复数据时,只需要保留重复数据中 empno 最大的那条记录
方法一:
先通过 group by ... having
找到重复数据中要保留的数据,再通过关联操作删掉未选中的数据。
DELETE a.* FROM emp a INNER JOIN (SELECT ename, MAX(empno) AS empno FROM emp GROUP BY ename HAVING COUNT(*) > 1) b ON b.ename = a.ename AND b.empno <> a.empno ;
方法二:
做自关联,然后删除关联上的数据中 empno 较小的记录。
DELETE a.* FROM emp a INNER JOIN emp b ON b.ename = a.ename AND a.empno < b.empno
如果存在比较多组的重复数据,且每组内的重复数据不是很多,使用方法二就比较高效。
如果重复数据非常少,使用方法二的就有可能出现性能问题,这时就应该选择方法一。
所以,应该先了解数据分布的情况,再选择合适的方法执行删数据操作。