原题题目
表:
Person
+-------------+---------+ | Column Name | Type | +-------------+---------+ | id | int | | email | varchar | +-------------+---------+ id 是该表的主键(具有唯一值的列)。 此表的每一行都包含一封电子邮件。电子邮件不包含大写字母。编写解决方案来报告所有重复的电子邮件。 请注意,可以保证电子邮件字段不为 NULL。
以 任意顺序 返回结果表。
结果格式如下例。
示例 1:
输入: Person 表: +----+---------+ | id | email | +----+---------+ | 1 | a@b.com | | 2 | c@d.com | | 3 | a@b.com | +----+---------+ 输出: +---------+ | Email | +---------+ | a@b.com | +---------+ 解释: a@b.com 出现了两次。
思考解决方案
首先,我们来分析下这个问题的几个关键点:
-
重复的定义:我们并不关心
id
这个字段,重点是email
字段。所以,只要电子邮件出现了两次或更多次,就算是重复。 -
结果要求:我们返回的结果只需要包含
email
,而且可以不按特定的顺序。
好了,问题就是这么简单了!但是要怎么解决呢?让我给你展示几个方法,从简单到高阶,我们一起来看看吧!
一、使用 GROUP BY
和 HAVING
这是最常见的解决方式,利用 SQL 的分组功能。首先,我们按照 email
对数据进行分组,然后使用 HAVING
子句来筛选出出现次数大于 1 的电子邮件。
SELECT email
FROM Person
GROUP BY email
HAVING COUNT(*) > 1;
解释:
-
GROUP BY email
:这会将相同的电子邮件分成一组。 -
HAVING COUNT(*) > 1
:我们用HAVING
来过滤出那些出现次数超过 1 次的电子邮件。
注意:使用GROUP BY进行分组之后,如果需要过滤只能使用HAVING,而不可以使用where
二、使用 JOIN
来查找重复
虽然我们不常用,但这也是一种非常有趣的解决方法。这里我们使用自连接(Self Join)来实现,连接 Person
表的两份数据并查找那些有相同 email
的行。
SELECT DISTINCT p1.email
FROM Person p1
JOIN Person p2 ON p1.email = p2.email AND p1.id != p2.id;
解释:
-
JOIN Person p2 ON p1.email = p2.email AND p1.id != p2.id
:这里我们把Person
表与它自己连接,条件是email
相同,但是id
不相同,这样就能找到重复的电子邮件。 -
DISTINCT
:我们使用DISTINCT
来去重,避免重复的电子邮件被多次显示。
这种方法看似复杂,但它利用了 SQL 中的连接操作,非常灵活。它的原理是通过对同一张表进行“自我联接”来查找重复。
三、 使用 WINDOW
函数(高级方法)
对于喜欢挑战 SQL 的高级技巧的人,可以考虑使用 WINDOW
函数。这个方法适用于支持窗口函数的数据库(例如 MySQL 8.0 及以上版本)。通过 COUNT()
窗口函数来对每个电子邮件进行计数。
SELECT email
FROM (
SELECT email, COUNT(*) OVER (PARTITION BY email) AS email_count
FROM Person
) AS email_counts
WHERE email_count > 1;
注意:这也是一种创建临时表的方式,还可以用以下的方式实现
select Email from
(
select Email, count(Email) as num
from Person
group by Email
) as statistic
where num > 1
;
四、总结
方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
方法 1 GROUP BY + HAVING | O(N) | O(N) | 推荐使用,最常见且高效 |
方法 2 JOIN | O(N^2) | O(N^2) | 不推荐大数据集,性能差 |
方法 3 WINDOW 函数 | O(N) | O(N) | 性能高,适合大数据集,支持窗口函数时推荐 |
大数据集推荐方案:
对于大数据集,方法 1(GROUP BY + HAVING
) 和 方法 3(WINDOW
函数) 是最优选择。GROUP BY
已经经过多年的优化,可以高效处理大多数查询。而 WINDOW
函数在计算上通常更为高效,尤其在数据量大时能够避免额外的分组操作。
总之,选择合适的方法不仅依赖于 SQL 本身的优化,还需要考虑数据库的硬件配置和处理能力。对于大数据集,尽量避免使用 JOIN
,因为它的性能开销非常大。