题目描述

✅ 196. 删除重复的电子邮箱

image-20260929090843453

image-20260929090843580

题意分析

直接删除 Person 表中重复邮箱的记录,让每种邮箱最终只保留 id 最小的一行。题目要求修改表内容,不能只查询去重后的结果。

id 是唯一标识。对任意一行来说,只要同一邮箱还存在更小的 id,它就不是应该保留的记录。

解法:自连接删除非最小记录

核心思路

[!blue]

用 p1、p2 两个别名引用同一张表。把 p1 看作待判断是否删除的记录,把 p2 看作证明它不是最小记录的同伴。

连接条件同时要求 p1.email = p2.email 和 p1.id > p2.id:前者保证两行属于同一邮箱组,后者保证 p1 确实有一个更小的同组记录。DELETE p1 则明确只删除匹配中的目标侧。

组内最小 id 找不到更小的同组记录,因此不会匹配删除条件;每个其他记录至少能与这个最小记录匹配一次,因此全部被删除。这样,每种邮箱恰好留下最小 id 的一行。

同一条 p1 可能匹配多个更小的 p2,但这些匹配都指向同一条待删记录,并不会改变删除结果。无需先把邮箱聚合成一行,也无需依赖记录的物理顺序。

解题步骤

  1. 给 Person 表分别起别名 p1、p2,构造自连接。
  2. 用邮箱相同限定同一组,再用 p1.id > p2.id 找出存在更小同伴的 p1。
  3. 在 DELETE 后指定别名 p1,删除这些非最小记录;没有重复邮箱的行自然不会被匹配。

代码实现

-- DELETE 后的 p1 指定删哪一侧;自连接找出「存在更小同伴」的行。
DELETE p1
FROM Person AS p1
JOIN Person AS p2
ON p1.email = p2.email
-- 存在更小的同邮箱记录时,才删除目标侧这一行。
AND p1.id > p2.id;

复杂度分析

设记录数为 n,实际执行成本取决于数据库的索引、连接方式和删除计划。

  • 时间复杂度:朴素自连接最坏为 $O(n^2)$;当大量记录属于同一邮箱时,也可能产生平方量级的匹配。合适的索引可以减少查找成本,但不能仅凭这段 SQL 断言固定的执行复杂度。
  • 空间复杂度:由连接缓冲和待删行记录方式决定;逻辑上存在很多匹配,不代表数据库一定会同时保存全部匹配行。

关键点总结

[!green]

  • 删除条件是“存在同邮箱且 id 更小的记录”,直接排除非最小者。
  • 最小记录没有可匹配的更小同伴,天然得到保留。
  • DELETE p1 指定目标侧,别名只是对同一张表中不同记录的引用。

易错点总结

[!yellow]

  • 把大于写成小于:会删除较小的记录,最终留下最大 id。
  • 用不等于替代大于:同组的不同记录会互相匹配,可能把整个重复组删掉。
  • 漏掉邮箱相等条件:会把其他邮箱中较小的 id 也当作删除依据。
  • 删除 p2 而不是 p1:目标方向反了,应该删除有更小同伴的较大记录。
  • 只写 SELECT DISTINCT:得到查询结果并不等于删除了表中的重复记录。

相似题目

题目 难度 关联与区别
182. 查找重复的电子邮箱 简单 先识别重复邮箱的分组,本题进一步只保留每组最小id并删除其他行。
178. 分数排名 中等 同样区分分组内记录次序与相同值,本题按id选择唯一保留行,不能仅按邮箱值去重后丢失身份。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2022/20433483
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!