SQL多字段去重核心是保留每组唯一组合的一条记录,GROUP BY最常用可控,配合聚合函数取代表值、子查询获取完整行或窗口函数ROW_NUMBER()按业务逻辑选行,DISTINCT仅适用于简单字段级去重。

SQL多字段去重,核心不是“删掉重复”,而是“保留每组唯一组合的一条记录”。GROUP BY 是最常用、最可控的实现方式,它不直接删除数据,而是通过分组 + 聚合(或搭配子查询/窗口函数)来提取去重后的结果。
用 GROUP BY 配合聚合函数取代表值
当你需要从重复组中选出一条“有代表性的记录”(比如最新时间、最大ID、非空名称),GROUP BY 必须搭配聚合函数使用:
- SELECT 字段1, 字段2, MAX(更新时间) AS 最后更新, COUNT(*) AS 出现次数
- FROM 表名
- GROUP BY 字段1, 字段2
这样就能按(字段1, 字段2)组合分组,每组只返回一行,并可带出该组内某个字段的极值或统计信息。注意:SELECT 中所有非聚合列,都必须出现在 GROUP BY 子句里。
用 GROUP BY + 子查询获取完整行数据
如果要去重后返回整行(不只是几个字段),单纯 GROUP BY 不够——因为无法保证其他字段值来自同一原始记录。这时常用子查询定位每组的“锚点”:
- SELECT t1.* FROM 表名 t1
- INNER JOIN (
- SELECT 字段1, 字段2, MAX(id) AS max_id
- FROM 表名
- GROUP BY 字段1, 字段2
- ) t2 ON t1.字段1 = t2.字段1 AND t1.字段2 = t2.字段2 AND t1.id = t2.max_id
这个例子按(字段1, 字段2)去重,并保留每组 id 最大的那条完整记录。关键是用子查询先确定“留哪一行”,再关联回原表取全字段。
用窗口函数更灵活地选行(推荐用于较新版本)
MySQL 8.0+、PostgreSQL、SQL Server 等支持窗口函数,可用 ROW_NUMBER() 直接编号去重组内的行:
- SELECT * FROM (
- SELECT *, ROW_NUMBER() OVER (PARTITION BY 字段1, 字段2 ORDER BY 更新时间 DESC) AS rn
- FROM 表名
- ) t WHERE rn = 1
PARTITION BY 定义多字段去重组,ORDER BY 决定组内排序逻辑(如按时间倒序,rn=1 就是最新一条)。这种方式语义清晰、无需自连接,适合复杂筛选场景。
注意:DISTINCT 是基础去重,但能力有限
DISTINCT 可对多字段联合去重:SELECT DISTINCT 字段1, 字段2, 字段3 FROM 表名。但它只能返回指定字段,不能带出其他字段,也不能控制“留哪一条”。所以真正需要保留完整行或按业务逻辑选行时,GROUP BY 或窗口函数才是主力。










