ROW_NUMBER() 不替代 DISTINCT 的速度,而是解决其无法按指定字段分组取最新记录的缺陷;它通过 PARTITION BY + ORDER BY 明确去重逻辑,配合索引可高效实现“每组保留一条”,而 DISTINCT 仅对 SELECT 全字段组合判重。

ROW_NUMBER() 不一定比 DISTINCT 快,但能避开 DISTINCT 的硬伤
直接说结论:ROW_NUMBER() 本身不提速,它解决的是 DISTINCT 根本做不到的事——比如“按 user_id 去重,但保留最新一条”。很多人测出 ROW_NUMBER() 更快,其实是把原本该用窗口函数的场景,错误地套了 DISTINCT,导致数据库被迫对全字段排序去重,白白多扫、多排、多占内存。
常见错误现象:SELECT DISTINCT user_id, created_at, status FROM orders 想取每个用户最新订单,结果返回了几十万行——因为三列组合几乎都不重复,DISTINCT 完全没起到“按 user_id 归并”的作用。
-
DISTINCT是整行比对:只要 SELECT 列表里所有值(含 NULL)完全一致,才算重复 -
ROW_NUMBER()是逻辑分组+排序编号:靠PARTITION BY user_id ORDER BY created_at DESC主动定义“谁和谁算一组” - 没有
PARTITION BY的ROW_NUMBER()等价于给全表编 1~N 号,毫无去重意义
什么时候 DISTINCT 会明显变慢?看执行计划这三点
真正拖慢 DISTINCT 的不是关键字本身,而是它隐含的执行路径。你得盯住执行计划里的这三个信号:
- 出现
Using temporary; Using filesort:说明数据库在内存不够时落盘排序,I/O 成瓶颈 -
rows扫描量远大于最终输出行数:比如扫 200 万行,只返回 5 千行,大概率是多列组合导致“假唯一” - 去重字段上没索引,且类型是
TEXT/VARCHAR(500):哈希或排序开销爆炸,尤其 MySQL 5.7 默认 sort_buffer_size 仅 2MB
举个例子:SELECT DISTINCT name, city, province FROM address,如果 name 平均长度 300 字符,又没建 INDEX(name, city, province),MySQL 很可能放弃哈希,改走外部归并排序,耗时翻倍。
ROW_NUMBER() 的性能关键在 PARTITION BY 字段是否可索引
ROW_NUMBER() 能稳的原因,是它把“去重逻辑”从黑盒变成白盒——你可以控制分组粒度和排序依据。但它的速度依然依赖底层支持:
- MySQL 8.0+ 和 PostgreSQL 会尝试用索引加速
PARTITION BY + ORDER BY组合,前提是索引顺序匹配(如INDEX(user_id, created_at)) - 如果
ORDER BY用了函数,比如ORDER BY DATE(created_at),索引失效,照样要排序 - SQLite 3.25.0+ 才支持
ROW_NUMBER(),老版本只能用自关联 +MAX(id),但会丢掉非聚合字段,还得再 JOIN 一次
一个典型高效写法:
SELECT id, user_id, amount, created_at
FROM (
SELECT id, user_id, amount, created_at,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders
WHERE created_at >= '2026-01-01'
) t
WHERE rn = 1;注意这里加了时间过滤条件——先缩小数据集,再开窗,比全表开窗快一个数量级。
DISTINCT 在什么场景下反而更干净?
别一上来就否定 DISTINCT。它在两类场景里既简洁又高效:
- 单字段去重查维度值,比如
SELECT DISTINCT category FROM products,配合category上的索引,走 index-only scan,基本无压力 - 多字段组合天然稀疏,比如
SELECT DISTINCT order_id, item_sku, qty,本来重复率就低于 0.1%,排序开销可忽略
最容易被忽略的一点:如果你只是要“统计去重后数量”,COUNT(DISTINCT col) 通常比 SELECT DISTINCT col FROM ... 再 COUNT() 快得多——前者可走近似算法(如 HyperLogLog),后者必须物化全部去重结果。

















