ROW_NUMBER() 不能直接去重,仅能编号;真正去重需配合子查询或CTE筛选rn=1的行,核心是PARTITION BY定义重复组、ORDER BY确保排序稳定,否则结果不可控。
ROW_NUMBER() 不能直接去重,它只能给行打序号;真正去重得靠外部筛选,比如 WHERE rn = 1。
它只是按指定排序规则给每一行分配一个唯一递增数字,哪怕两行数据完全相同,只要排序键(ORDER BY)能区分它们(或用了不确定排序),ROW_NUMBER() 也会分出 1、2、3…… 所以必须配合子查询或 CTE,再过滤出序号为 1 的行。
常见错误现象:SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) FROM orders; 这条语句只是加了一列序号,并没有减少行数。
这是最常用、也最易理解的模式。核心是先算出每组内的序号,再在外层只取 rn = 1 的记录。
ORDER BY updated_at DESC,然后取 rn = 1
ORDER BY created_at ASC
NULLS LAST 或 NULLS FIRST(PostgreSQL/Oracle 支持;MySQL 8.0+ 不支持该语法,需用 IS NULL 模拟)WITH ranked AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY updated_at DESC, id DESC ) AS rn FROM users)SELECT id, user_id, email, updated_atFROM rankedWHERE rn = 1;
DISTINCT 是基于整行值去重,GROUP BY 必须配聚合函数,而 ROW_NUMBER() 方案能「保留原始行的任意字段」——只要你排序逻辑合理,就能挑出你想要的那条完整记录。
user_id 分组,取 updated_at 最新那条,同时拿到它的 status、source、id 等所有字段,GROUP BY 做不到这点(除非全写进 GROUP BY,那就失去去重意义)ROW_NUMBER() 需要排序,大数据量时注意 PARTITION BY 和 ORDER BY 字段是否有索引;MySQL 8.0+、PostgreSQL、SQL Server 都支持,但 SQLite 目前(3.45 仍不支持窗口函数)ERROR 1064: You have an error in your SQL syntax,其实是版本太低当 PARTITION BY 内多行的排序字段值完全相同时(比如同个 user_id 下几条记录 updated_at 都是 '2024-01-01 00:00:00'),ROW_NUMBER() 的结果是不确定的——数据库可能每次返回不同“首行”。
解决办法只有一个:在 ORDER BY 里补一个能保证唯一性的字段(如主键 id),否则去重结果不可复现。
错误写法:ORDER BY updated_at DESC → 同时间戳下哪条被选中是随机的
正确写法:ORDER BY updated_at DESC, id DESC → 时间相同时,取 id 最大的那条,稳定可预期
克里斯托弗·诺兰作品《致命魔术》将重返影院
TP-Link Deco 7 Pro BE10000 Wi-Fi 7 全屋 Mesh 系统 6.4 折优惠现享
《自由足球怀旧服》大乱斗模式开启:强势中场登场:S3赛季任务及奖励重磅更新!
网易冷兵器新作《诡影藏锋》实机首曝:女鬼魅惑,恐怖拉满
炉石传说: Escape from Violet Hold 今日上线——探索游戏背后的人生哲理
亚马逊开启精选 4K 铁盒版促销:《回到未来》《侏罗纪世界》三部曲等作品迎来优惠