mysql删除重复数据仅保留一条包含null的问题

本文描述了一个关于在MySQL中删除重复数据时遇到的问题,由于NULL值的存在导致删除操作未成功。通过分析和排查,发现是由于IN运算符在处理NULL值时的特殊性。解决方案是使用NOT IN子句结合GROUP BY和MIN()函数来正确删除重复数据。
摘要由CSDN通过智能技术生成

问题描述

前两天小灰清理一张表的赃数据,需要删除重复数据,仅保留一条。于是,小灰写了一条删除重复数据的sql语句,但是执行之后,发现仍然有重复数据。经排查是重复数据中有null值引起的。为了清晰直观的说明问题,这里新建一张testtable表:

CREATE TABLE `testtable` (
  `id` int(11) NOT NULL,
  `type` varchar(45) NOT NULL,
  `state` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

其中type可能的取值为’a’、‘b’、‘c’,state可能的取值为’0’、‘1’、NULL,现表中存在重复数据(两行数据中type值和state值分别相等),select * from testtable order by type,state;查询数据如下:

idtypestate
10aNULL
1a0
9a1
11bNULL
4b0
3b0
2b1
5cNULL
7cNULL
6c0
8c1

小灰写的删除语句是:

delete from testtable where (type,state) in (
	select t.type, t.state from (
		select type,state from testtable 
        group by type,state
        having count(1) > 1
    ) t
) and id not in(
	select dt.minid from(
		select min(id) as minid from testtable
        group by type,state
        
    ) dt
)

执行完之后,再次查询select * from testtable order by type,state;结果如下:

idtypestate
10aNULL
1a0
9a1
11bNULL
3b0
2b1
5cNULL
7cNULL
6c0
8c1

大家注意到此时还是有两条重复数据id分别为5和7。

问题排查

大家看到表中数据很容易怀疑是不是两条重复数据中存在NULL的问题。但其实真实环境中,字段很多,数据量也大,小灰并没有第一眼发现问题所在。于是查询了一下delete语句中where条件后面的in子句:

select t.type, t.state from (
		select type,state from testtable 
        group by type,state
        having count(1) > 1
    ) t

可以查到重复数据:

typestate
cNULL

但是执行delete语句确没有删除掉:

delete from testtable where (type,state) in (
	select t.type, t.state from (
		select type,state from testtable 
        group by type,state
        having count(1) > 1
    ) t
)

执行结果如下:
11:02:27 delete from testtable where (type,state) in ( select t.type, t.state from ( select type,state from testtable group by type,state having count(1) > 1 ) t ) 0 row(s) affected 0.053 sec
所以问题出在where (type, state) in(…) 这里。
我们知道in运算符用来判断操作数是否为in列表中的一个值,将操作数依次与列表中的元素比较,如果相等则返回1,如果都不相等则返回0。但是NULL比较特殊,如果操作数中有NULL值,不管in列表中是否有NULL值,都返回NULL。如果操作数中没有NULL,右边in列表中没有与其相等的值并且有NULL值,此时不返回0而是返回NULL。如:

select 2 in ('2'), 2 in ('3'), NULL in ('2'), NULL in ('2', NULL), 2 in ('3', NULL), 2 in('2', NULL)
2 in (‘2’)2 in (‘3’)NULL in (‘2’)NULL in (‘2’, NULL)2 in (‘3’, NULL)2 in(‘2’, NULL)
10NULLNULLNULL1

所以我们平时判断一个值是否为NULL,应该用is NULL、is not NULL,而不能用=NULL、!= NULL。
前面例子中,存在重复数据的记录中“state”字段的值为NULL,所以执行delete语句的时候,循环到id为7的记录时,该记录的state字段值为NULL,判断是否在in列表中包含时,虽然in列表的记录中,存在state字段值为NULL的记录,但是in运算结果返回NULL,认为in列表中不存在与id为7的记录相等的值,导致最终没有删除掉该重复记录。

问题解决

问题已经清楚了,那如何解决呢?其实我们可以换个思路,要删除掉重复记录,就是删除唯一记录之外的所有记录。由于group by结果中非group by字段只返回一条,也符合上述原则。所以,删除语句改写如下:

delete from testtable where id not in (
	select minid from (
		select min(id) as minid from testtable group by type,state 
    ) t
)

参考
[1]: https://dev.mysql.com/doc/refman/8.0/en/working-with-null.html
[2]: https://dev.mysql.com/doc/refman/8.0/en/problems-with-null.html
[3]: https://blog.csdn.net/n950814abc/article/details/82284838

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 可以使用以下 SQL 语句删除重复数据保留一条: ``` DELETE t1 FROM 表名 t1, 表名 t2 WHERE t1.id > t2.id AND t1.字段名 = t2.字段名; ``` 其中,`表名` 为需要删除重复数据的表名,`字段名` 为用于判断重复的字段名,`id` 为表中的主键或唯一标识符。该 SQL 语句会将所有重复数据保留一条删除其余重复数据。 ### 回答2: 要删除重复数据保留一条,可以使用MySQLDELETE语句和子查询来实现。 首先,可以使用如下的SELECT语句来查询出所有重复数据: SELECT col1, col2, ..., coln, COUNT(*) AS count FROM table_name GROUP BY col1, col2, ..., coln HAVING COUNT(*) > 1; 这个查询用于找到所有有重复数据的列,并通过GROUP BY和HAVING子句筛选出重复数据。 接下来,可以使用DELETE语句和子查询来删除重复数据,只保留一条DELETE FROM table_name WHERE (col1, col2, ..., coln) NOT IN (SELECT col1, col2, ..., coln FROM table_name GROUP BY col1, col2, ..., coln HAVING COUNT(*) > 1); 这个DELETE语句会删除表中除了第一次出现的重复数据以外的所有重复数据。 注意,在使用这个方法之前,一定要先备份好数据,以免误删数据。 ### 回答3: 要删除MySQL中的重复数据保留一条,可以使用以下的步骤: 1. 首先,我们需要找出重复数据。可以使用GROUP BY和HAVING语句来找到重复数据。例如,假设我们要删除表中的重复email字段,可以使用以下语句: SELECT email, COUNT(*) FROM table_name GROUP BY email HAVING COUNT(*) > 1; 这将返回所有重复的email及其重复的次数。 2. 根据找到的重复数据,我们可以选择保留其中的一条,并删除其他重复数据。可以使用DELETE语句来删除重复数据。例如,假设我们要保留COUNT(*)最大的那条数据,可以使用以下语句: DELETE FROM table_name WHERE email IN (SELECT email FROM table_name GROUP BY email HAVING COUNT(*) > 1) AND id NOT IN (SELECT MAX(id) FROM table_name GROUP BY email HAVING COUNT(*) > 1); 这将删除除了COUNT(*)最大的那条数据以外的其他重复数据。 3. 最后,我们可以再次运行第一步的查询语句来验证是否已成功删除重复数据。如果查询结果为空,说明已成功删除重复数据保留一条。 需要注意的是,执行此操作前请务必备份数据库,以防意外数据损失。另外,根据实际情况,可能需要针对具体的表和字段进行调整。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值