首页/数据/SQL 速查/窗口函数去重:每组只留一行
聚合与窗口函数54 条

窗口函数去重:每组只留一行

去重取最新/最早一条的通用写法:PARTITION BY 去重键 + ORDER BY 排序 + rn=1;比 GROUP BY 更容易保留整行信息。

SQL 速查数据更新于 2026-10-13

窗口函数去重是什么

给同一去重键的行按“保留优先级”排序编号,再只取编号为 1 的行,是“每组只留最新或最优先一条”的通用解法。相比 GROUP BY,它能保留整行的所有列,不受聚合列限制;相比相关子查询,它通常只扫一遍表。

基本信息

核心写法ROW_NUMBER() OVER (PARTITION BY 去重键 ORDER BY 优先级) AS rn,外层取 rn = 1
保留整行先编号再筛行,明细列全都能带上
排序优先级业务时间加主键,保证同值行也有确定顺序
与 GROUP BY 的区别GROUP BY 每组一行但只能出聚合列,窗口去重能保留明细列
去重键选择邮箱、手机号、业务编号,需要先与业务确认口径
删除重复MySQL 可用自连接或子查询句柄配合 DELETE 批量删除

每组只留最新一行

每组只留最新一行
WITH ranked AS (
    SELECT u.*,
           ROW_NUMBER() OVER (PARTITION BY u.email       -- 去重键
                              ORDER BY u.updated_at DESC, -- 优先级:新的优先
                                       u.id DESC)         -- 次级键,保证顺序确定
             AS rn
    FROM users u
)
SELECT id, email, updated_at
FROM ranked
WHERE rn = 1;   -- 每个邮箱只留一行

GROUP BY 做不到的取整行

GROUP BY 做不到的取整行
-- 只想要最大更新时间:GROUP BY 足够
SELECT email, MAX(updated_at) FROM users GROUP BY email;

-- 想同时取出那条记录的其它列:GROUP BY 表达不了,必须用窗口去重
SELECT id, email, updated_at
FROM (
  SELECT u.*,
         ROW_NUMBER() OVER (PARTITION BY email ORDER BY updated_at DESC, id DESC) AS rn
  FROM users u
) t
WHERE rn = 1;   -- Oracle/MySQL 8/PostgreSQL 都支持这种写法

去重写法对比

写法输出适用场景
GROUP BY每组一行、仅聚合列只需要汇总值
ROW_NUMBER 取 1每组一行、可保留全部列取整行,最常用
RANK / DENSE_RANK并列时可能每组多行要求并列都保留
相关子查询每组一行、逐行判定数据量小、逻辑直观
DISTINCT全列完全相同的行去重不需要优先级排序

典型使用场景

  • 用户表按邮箱去重,保留最后更新的那条记录
  • 订单按业务编号去重,保留状态最靠后的一条
  • 多来源合并后按业务键去重,保留优先级最高的来源
  • 日志表取每个会话的首条与末条记录
  • 清理重复数据前先选出待删清单,核对无误再执行删除
  • 同步任务按幂等键只取一行,避免向下游重复写入
先编号再过滤与先过滤再编号结果不同:先 WHERE 再编号,被过滤掉的行就不参与优先级判断。按业务决定顺序——要把它们排除在选择之外,还是只排除在展示之外。
排序键不足以唯一确定时“最新”是随机的:写入时间精度相同、状态并列都可能让同一组每次取到不同行。排序里补一列主键,结果才可复现。

易错点

  • 漏写外层 rn = 1:每个组的所有行都被输出,等于没去重
  • 排序键不够唯一:同秒写入的记录每次执行可能取到不同行,需补主键做次级排序
  • 去重键选错:按邮箱还是按手机号结果完全不同,删数据前先与业务确认并留备份
  • 删除重复行不分批:一次大事务会撑大回滚段并阻塞线上写入,先选出待删主键清单再分批删

数据来源与参考

  • MySQL 8.0 Reference Manual · Window Functions
  • PostgreSQL Documentation · Window Functions
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌