img.png

在数据库中,有时我们会遇到这种情况:一张表中存在多个记录拥有相同的邮箱地址(email),但我们只希望保留每组邮箱中 ID 最小 的那一条记录,其余的都删除。

本文将展示两种方式来实现这一需求。


表结构与示例数据

我们使用如下表结构和测试数据:

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE `person` (
`id` INT NOT NULL,
`email` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

INSERT INTO person (id, email)
VALUES
(1, '[email protected]'),
(2, '[email protected]'),
(3, '[email protected]');

插入数据后的表如下所示:

1
2
3
4
5
6
7
+----+------------------+
| id | email |
+----+------------------+
| 1 | [email protected] |
| 2 | [email protected] |
| 3 | [email protected] |
+----+------------------+

目标

我们希望删除邮箱重复的记录,只保留每个邮箱中 id 最小的那一条。


方法一:使用子查询方式

第一步:找出每组邮箱中 id 最小的记录

1
SELECT MIN(id) AS id FROM person GROUP BY email;

输出为我们需要保留的 id 值:

1
2
3
4
5
6
+----+
| id |
+----+
| 1 |
| 2 |
+----+

第二步:删除其它记录

使用子查询删除不在上述结果中的记录:

1
2
3
4
5
6
DELETE FROM person
WHERE id NOT IN (
SELECT id FROM (
SELECT MIN(id) AS id FROM person GROUP BY email
) AS t
);

注意:这里使用了两层子查询,因为 MySQL 不允许直接在 DELETE 中引用同一个表的子查询结果。使用中间别名 t 进行隔离是解决这个限制的常见方法。


方法二:使用内连接方式(推荐)

MySQL 支持使用自连接来判断并删除重复数据,语法更简洁,执行效率也更高:

1
2
3
DELETE p1 FROM person p1,
person p2
WHERE p1.email = p2.email AND p1.id > p2.id;

这条语句的意思是:当两条记录 email 相同,但 p1.id > p2.id 时,说明 p1 是重复项,将其删除。最终仅保留 id 最小的那条。


最终结果

不管使用哪种方法,最终 person 表将只剩下每个 email 的最小 id

1
2
3
4
5
6
+----+------------------+
| id | email |
+----+------------------+
| 1 | [email protected] |
| 2 | [email protected] |
+----+------------------+

总结

  1. 可以使用 GROUP BY + MIN(id) 再配合 DELETE ... NOT IN (...) 来实现去重;
  2. 更推荐使用 DELETE JOIN 的方式,通过自连接判断并删除重复数据;
  3. 在处理大数据表时,建议使用后者以提升性能;