sql去重首选 DISTINCT(仅需唯一值)或 GROUP BY(需聚合统计),窗口函数用于保留最新完整记录,删除重复数据须谨慎比对并备份。

SQL 去重最常用的是 DISTINCT 和 GROUP BY,选哪个取决于你要保留什么信息——只看唯一值就用 DISTINCT,要聚合计算(比如统计次数、求平均)就得用 GROUP BY。
场景一:查出所有不重复的用户名
比如用户表 users 里 name 字段有大量重复,你只想知道都有哪些名字:
select DISTINCT name FROM users;
✅ 简单直接,返回结果里每个 name 只出现一次。
⚠️ 注意:DISTINCT 作用于整行(所有 SELECT 的字段组合),不是单个字段。如果写 SELECT DISTINCT name, age,那 (张三,25) 和 (张三,28) 就算两条不同记录。
场景二:查重复数据本身(找谁重复了)
光知道“有重复”不够,常需要找出具体哪些记录重复、重复几次。这时用 GROUP BY 配合 HAVING:
- 查出所有重复的 邮箱 及重复次数:
SELECT email, COUNT(*) AS cnt FROM users GROUP BY email HAVING COUNT(*) > 1;
✅ 这样能定位问题数据,方便后续清理或告警。
? 小技巧:加 ORDER BY cnt DESC 可把重复最多的排前面,优先处理高频异常。
场景三:去重后取每组最新 / 某一条完整记录
DISTINCT 和 GROUP BY 都不能直接返回“某条完整记录”(比如重复 邮箱 中注册时间最新的那个用户)。这时得用窗口函数或关联子查询:
- 用 ROW_NUMBER() 标序号,再筛序号 = 1 的:
SELECT id, name, email, created_at FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY email ORDER BY created_at DESC) AS rn FROM users ) t WHERE rn = 1;
✅ 每个 email 只留最新的一条完整数据。
? 替换 ORDER BY created_at DESC 可改成按 id、update_time 等排序,灵活控制“留哪条”。
场景四:删除重复数据(只保留一条)
真正清理数据时,推荐先用上面方法确认无误,再执行删除。安全做法是借助自连接或主键比较:
- 假设 users 表有自增主键 id,按 email 去重,保留 id 最小的那条:
DELETE u1 FROM users u1 INNER JOIN users u2 ON u1.email = u2.email AND u1.id > u2.id;
✅ 删掉所有“同邮箱但 id 更大”的记录,等效于每组只留 id 最小的。
⚠️ 务必先备份!生产环境建议先在测试库验证 SQL 效果。
基本上就这些。DISTINCT 快速看去重结果,GROUP BY 适合统计分析,窗口函数解决“留哪条”的业务逻辑,删数据则要谨慎比对。用对方法,去重不复杂但容易忽略细节。