删除重复邮箱:只保留 Id 最小的一条
LeetCode 196 · DELETE 自连接、去重保留规则
面试怎么回答
我会用自连接的多表 DELETE:Person p1 和 p2 按邮箱相等关联,删除 p1.Id > p2.Id 的行,留下的就是每个邮箱的最小 Id。真实业务去重我会先在临时表里用 ROW_NUMBER 标记要删的行,核对数量后再删,并保留可回滚的备份。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
题意
直接修改人员表:同一个邮箱出现多次时,只保留 Id 最小的那一行,其余删除。
表结构
- Person:Id(主键)、Email
思路
用多表 DELETE 的自连接:把表关联自己,邮箱相同且 Id 更大的那一份就是要删除的重复行。也可以先算出每个邮箱的最小 Id,再删除不在其中的行,但 MySQL 不允许在 DELETE 的子查询里直接引用正在删除的表,需要多包一层派生表。
参考解法
DELETE p1 FROM Person p1,
Person p2
WHERE
p1.Email = p2.Email AND p1.Id > p2.Id
另一种写法:先算每个邮箱的最小 Id,再删其余行(注意外面多包一层)
DELETE FROM Person
WHERE Id NOT IN (
SELECT id FROM (SELECT MIN(Id) AS id FROM Person GROUP BY Email) t
)
易错点
- MySQL 里
DELETE FROM Person WHERE Id NOT IN (SELECT MIN(Id) FROM Person GROUP BY Email)会报错,要把子查询再包一层。 - 这是修改数据的操作,生产环境要先 SELECT 出将被删除的行确认一遍,或者在备份表上执行。