题目
手写 SQL:删除表中重复的数据,只保留其中一条记录
答案
假设有一个表名为 `user`,包含以下列:
| Column Name | Data Type |
| ----------- | --------- |
| id | int |
| name | varchar |
| age | int |
| email | varchar |
下面是一个示例的 `user` 表:
| id | name | age | email |
| ---- | ------- | ---- | ------------------------------------------------- |
| 1 | Alice | 25 | [alice@example.com](mailto:alice@example.com) |
| 2 | Bob | 30 | [bob@example.com](mailto:bob@example.com) |
| 3 | Charlie | 25 | [charlie@example.com](mailto:charlie@example.com) |
| 4 | David | 30 | [david@example.com](mailto:david@example.com) |
| 5 | Alice | 25 | [alice@example.com](mailto:alice@example.com) |
如果我们想要删除重复数据,也就是`name`、`age`、`email` 三列都相同的行,很多同学都会写出如下所示的 SQL:
```sql
DELETE FROM user
WHERE id NOT IN (
SELECT MIN(id)
FROM user
GROUP BY name, age, email
)
```
但注意,这个 SQL 是无法执行成功的!因为**涉及到正在被查询的表的同时修改**,换句话说**在同一个表中进行 DELETE 操作的子查询会导致 SQL 语句执行失败**。
正确的方法应该是使用临时表,而不是直接在同一个表上进行 DELETE 操作:
```sql
DELETE FROM user
WHERE id NOT IN (
SELECT id FROM (
SELECT MIN(id) as id
FROM user
GROUP BY name, age, email
) AS t
);
```
这个SQL语句中,内部的子查询会返回每组(name、age、email)中id最小的记录的集合,外部的DELETE语句会删除不在这个集合中的记录。这样就能够实现删除重复记录的目的,而且避免了在同一个表上同时查询和 DELETE。
执行完上述 SQL 语句后,表 `students` 中仅剩下如下数据:
| id | name | age |
| ---- | ------- | ---- |
| 1 | Alice | 18 |
| 2 | Bob | 19 |
| 3 | Charlie | 20 |
| 4 | David | 21 |
其中,原来 `id` 值为 5 的那条记录被删除了。
