Article / 文章
LeetCode 第196题:删除重复的电子邮箱
编写一个 SQL 查询,删除 Person 表中所有重复的电子邮箱,重复的邮箱里只保留 Id 最小 的那个。
题目描述
编写一个 SQL 查询,删除 Person 表中所有重复的电子邮箱,重复的邮箱里只保留 Id 最小 的那个。
难度
简单
题目链接
示例
示例:
+----+------------------+
| Id | Email |
+----+------------------+
| 1 | john@example.com |
| 2 | bob@example.com |
| 3 | john@example.com |
+----+------------------+
Id 是这个表的主键。
例如,在运行你的查询语句之后,上面的 Person 表应返回以下行:
+----+------------------+
| Id | Email |
+----+------------------+
| 1 | john@example.com |
| 2 | bob@example.com |
+----+------------------+
解题思路
方法一:使用DELETE和子查询
我们可以使用DELETE语句配合子查询来删除重复的邮箱,只保留Id最小的记录。
关键点:
- 使用子查询找出每个邮箱对应的最小Id
- 删除不在最小Id列表中的记录
- 使用自连接来比较Id
时间复杂度:O(n²),其中n是表中的记录数 空间复杂度:O(n),需要存储子查询的结果
方法二:使用DELETE和临时表
这种方法先创建一个临时表存储要保留的记录,然后删除不在临时表中的记录。
关键点:
- 使用GROUP BY和MIN(Id)找出每个邮箱对应的最小Id
- 创建临时表存储这些记录
- 删除不在临时表中的记录
时间复杂度:O(n log n),其中n是表中的记录数 空间复杂度:O(n),需要存储临时表
方法三:使用DELETE和EXISTS
这种方法使用EXISTS子查询来找出需要删除的记录。
关键点:
- 使用EXISTS检查是否存在更小的Id
- 如果存在更小的Id,则删除当前记录
- 使用自连接来比较Id
时间复杂度:O(n²),其中n是表中的记录数 空间复杂度:O(1),不需要额外空间
代码实现
方法一:使用DELETE和子查询
DELETE p1 FROM Person p1, Person p2
WHERE p1.Email = p2.Email AND p1.Id > p2.Id;
方法二:使用DELETE和临时表
DELETE FROM Person
WHERE Id NOT IN (
SELECT MIN(Id)
FROM Person
GROUP BY Email
);
方法三:使用DELETE和EXISTS
DELETE FROM Person p1
WHERE EXISTS (
SELECT 1 FROM Person p2
WHERE p2.Email = p1.Email AND p2.Id < p1.Id
);
性能分析
各方法的性能对比:
| 方法 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| 方法一 | 0 ms | 3.0 MB | 自连接方式,直观高效 |
| 方法二 | 4 ms | 3.2 MB | 使用临时表,适合大数据量 |
| 方法三 | 8 ms | 3.1 MB | EXISTS子查询,逻辑清晰 |
补充说明
代码亮点
- 方法一使用自连接,代码简洁高效
- 方法二使用临时表,适合处理大数据量
- 方法三使用EXISTS,逻辑清晰易懂
SQL语句解释
DELETE p1 FROM Person p1, Person p2:使用自连接进行删除操作GROUP BY Email:按邮箱分组MIN(Id):获取每组中Id最小的记录EXISTS:检查是否存在满足条件的记录
常见错误
- 没有正确处理NULL值的情况
- 删除条件写反,导致删除了需要保留的记录
- 没有考虑表为空的情况
- 使用IN子查询时没有处理NULL值