rank()和dense_rank()会导致top n查询返回超量行,因为二者对并列值赋予相同排名:若10人同分并列第1,dense_rank()全赋值1,where dense_rank

为什么RANK()和DENSE_RANK()会让Top N查询返回超量行
用RANK()或DENSE_RANK()做“前N名”筛选时,返回行数可能远超N——这不是bug,是函数语义决定的。比如10人同分并列第1,DENSE_RANK()全给1,WHERE rank_num 就返回全部10行;<code>RANK()也一样,只要并列值落在≤N范围内,就会全收进来。真正要“最多取N行”,必须用ROW_NUMBER()。
ROW_NUMBER()才是严格控量的正确选择
ROW_NUMBER()不认值是否相等,只按排序顺序硬编1、2、3……它保证每组最多N行,不会因并列膨胀。但要注意:它不体现业务意义上的“并列”,只是编号工具。
- 必须写
ORDER BY,否则报错(MySQL 8.0+、PostgreSQL、SQL Server均强制) - 排序字段建议加唯一键防随机,例如
ORDER BY total_amount DESC, order_id ASC - 不能在
WHERE里直接用ROW_NUMBER(),得套子查询或CTE - 示例:
SELECT customer_id, order_id, total_amount FROM ( SELECT customer_id, order_id, total_amount, ROW_NUMBER() OVER ( PARTITION BY customer_id ORDER BY total_amount DESC, order_id ASC ) AS rn FROM orders ) t WHERE rn
别在WHERE里直接引用窗口函数别名
写WHERE rn 会报错<code>column "rn" does not exist,因为SQL执行顺序是WHERE → GROUP BY → HAVING → SELECT → WINDOW → ORDER BY → LIMIT,窗口函数在WHERE之后才计算。所有依赖排名的过滤都必须外层再套一层。
- CTE比嵌套子查询更易读,尤其当基础数据需预过滤(如
WHERE status = 'completed') - MySQL要求CTE里的子查询显式起别名,否则报
ERROR 1248 - 别名不能重复,也不能用保留字,比如
rank在某些版本会冲突
大表上没索引时性能会断崖下跌
ROW_NUMBER()需要对每个分组完整排序,如果PARTITION BY字段和ORDER BY字段没联合索引,数据库就得扫全表+磁盘排序。特别是JOIN后结果集变大,问题更明显。
- 索引应覆盖
PARTITION BY和ORDER BY字段,例如(customer_id, total_amount) - 如果排序字段含NULL,MySQL不支持
NULLS LAST,得用COALESCE(total_amount, -999999)兜底 -
PARTITION BY字段为NULL时,所有NULL会被归为同一组——这是标准行为,但常被忽略
EXPLAIN ANALYZE看执行计划里有没有Sort Method: external merge,有就是磁盘排序在拖慢你。











