近期遇到需要删除一张表中重复数据的需求,解决思路如下:
先分组查询,得到每组中的一条记录,这些记录就是需要保留的记录。然后在此基础上使用left join 查询,得到需要删除的数据。最后删除这些数据。下面举例说明:
假设需要操作的表为 usermessage,结构如下:
该表记录了用户的消息,表内容如下:
一共两个用户,每个用户都有4条消息,每条消息后面都有时间戳(updatetime)。
我们的需求是:删除该表中重复记录,每个用户只保留最近的一条消息。
按解决思路,首先分组查询出每个用户的最近(updatetime最大)的一条消息。
SELECT userId, message, max(updatetime) updatetime FROM usermessage group by userId
结果如下:
然后在此基础上查询需要删除的数据,
select * from usermessage a
left join (SELECT userId, message, max(updatetime) updatetime
FROM usermessage group by userId) b
on a.userid = b.userid and a.updatetime = b.updatetime
where b.updatetime is null;
结果如下:
对比原表可以看出,这就是需要删除的数据。
最后,我们来删除这些数据。注意:因为这里使用了left join ,所以还不能直接将select 换为 delete,会报错:
而应该用下面的代码:
delete a from usermessage a
left join (SELECT userId, message, max(updatetime) updatetime
FROM usermessage group by userId) b
on a.userid = b.userid and a.updatetime = b.updatetime
where b.updatetime is null;
执行结果如下:
可以看到删除了6条记录,这正是我们需要的结果,这时你查询原表,就发现任务完成了。
下面将该表的建表及测试数据插入sql 贴出来,方便大家验证:
DROP TABLE IF EXISTS `usermessage`;
CREATE TABLE `usermessage` (
`userId` int(11) NOT NULL COMMENT '用户ID',
`message` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '消息',
`updatetime` bigint(20) NOT NULL COMMENT '更新时间',
PRIMARY KEY (`userId`, `updatetime`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of usermessage
-- ----------------------------
INSERT INTO `usermessage` VALUES (1001, '我来了', 1685234289123);
INSERT INTO `usermessage` VALUES (1001, '我是谁', 1692142049544);
INSERT INTO `usermessage` VALUES (1001, '我还是我', 1692240089846);
INSERT INTO `usermessage` VALUES (1001, '我在这里', 1692240189247);
INSERT INTO `usermessage` VALUES (1002, '你在吗?', 1646240084562);
INSERT INTO `usermessage` VALUES (1002, '你是谁', 1691142389469);
INSERT INTO `usermessage` VALUES (1002, '你还是你', 1692042249856);
INSERT INTO `usermessage` VALUES (1002, '你在哪里', 1692140439436);
补充说明:有一些文章中介绍使用 in 的方式来筛选,实测发现效率很低,使用left join是效率比较高的一种方式。
参考文献:
【mysql】mysql删除重复记录并且只保留一条_mysql删除完全重复数据只保留一条_千g的博客-CSDN博客
mysql delete from ... left join .. on .._mysql delete from join_torpidcat的博客-CSDN博客