位置:首页 > SQL > SQL子查询查找重复数据并保留一条记录的方法

SQL子查询查找重复数据并保留一条记录的方法

时间:2026-08-18  |  作者:电竞小硕  |  阅读:0

用GROUP BY + HA VING可快速定位重复值,如SELECT email, COUNT() FROM users GROUP BY email HA VING COUNT() > 1;需注意WHERE不能用聚合函数,HA VING才是关键。

SQL子查询如何查找重复数据并保留一条

GROUP BY + HA VING 快速定位重复值

想把重复记录揪出来,最省事的做法通常不是写一层套一层的子查询,而是直接上分组聚合。比如,要找出 users 表里邮箱重复的记录,可以这样写:

SELECT email, COUNT(*) 
FROM users 
GROUP BY email 
HA VING COUNT(*) > 1;
这里真正的关键点在 HA VING——WHERE 不能处理聚合函数,HA VING 才可以。所以,一旦漏掉 HA VING,或者误写成 WHERE COUNT(*) > 1,SQL 会当场报错。

用子查询配合 ROW_NUMBER() 删除重复但留一条

真正需要“保留一条、删其余”的场景,靠 GROUP BY 不够,得借助窗口函数。MySQL 8.0+、PostgreSQL、SQL Server 都支持:

DELETE FROM users 
WHERE id NOT IN (
SELECT id FROM (
SELECT id, ROW_NUMBER() OVER (PARTITION BY email ORDER BY id) AS rn 
FROM users
) t 
WHERE t.rn = 1
);
重点在 PARTITION BY email:按邮箱分组;ORDER BY id 决定哪条被保留(最小 id)。如果表没主键或 id 不唯一,换成有业务意义的字段,比如 created_at

低版本 MySQL(5.7 及以下)怎么绕过窗口函数

如果环境里没有 ROW_NUMBER(),那就只能退一步,用自连接或相关子查询来处理。办法虽然“笨”一点,性能也确实不占优,但该做的事还是能做成。比如,想找出每组重复数据里除“最小 id”之外的那些记录,可以这样写:

SELECT u1.* 
FROM users u1 
INNER JOIN users u2 
ON u1.email = u2.email AND u1.id > u2.id;
这条查询返回的,正是后续需要删除的那些行。要真正执行删除,也很直接,把 SELECT 改成 DELETE u1 就行。这里有个细节不能忽视:一定要带上 u1.id > u2.id 这个条件,否则记录会匹配到自己,误删风险很高。另外,这种写法在大表上通常会比较慢,最好提前加上 INDEX(email)

EXISTS 子查询判断某条是否为重复中的“首条”

某些场景不需要删数据,只是标记或过滤——比如只查每个邮箱最早注册的用户。这时用 EXISTS 更清晰:

SELECT * FROM users u1 
WHERE NOT EXISTS (
SELECT 1 FROM users u2 
WHERE u2.email = u1.email AND u2.id < u1.id
);
逻辑是:“不存在另一个同邮箱且 id 更小的记录”,那当前这条就是该邮箱的第一条。这个写法兼容所有 SQL 方言,但要注意 u2.id < u1.id 不能写反,否则逻辑全错。

真正麻烦的不是语法,而是“保留哪一条”的业务定义没对齐:是按时间?按主键?还是某个状态字段?一旦条件模糊,ORDER BY 或比较逻辑就会出偏差,后面所有操作都白做。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多