row_number()实现分组内连续排名需用partition by分组、order by排序,如select name,dept,score,row_number()over(partition by dept order by score desc)as rn from employees;相同score仍分配不同序号;并列需求应改用rank()或dense_rank()。

用 ROW_NUMBER() 实现分组内连续排名
需要按某个字段分组(比如部门),再在每组内按成绩从高到低排 1、2、3……就用 ROW_NUMBER() 配合 PARTITION BY。它保证每组内序号不重复、不间断。
常见错误是漏写 ORDER BY 子句——ROW_NUMBER() 必须带 ORDER BY,否则报错;另外 PARTITION BY 和 ORDER BY 都作用于窗口,不能直接写在 WHERE 后面。
SELECT name, dept, score, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY score DESC) AS rn FROM employees;- 如果 score 相同,
ROW_NUMBER()仍会强行分配不同序号(比如 1、2、3),不考虑并列 - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 3.25+ 也支持
处理并列情况:改用 RANK() 或 DENSE_RANK()
当同一组里有相同 score 时,你可能希望它们并列第 1 名,下一名是第 3 名(跳过 2)——用 RANK();如果希望并列第 1 名后下一名是第 2 名(不跳号),就用 DENSE_RANK()。
三者区别不在语法,而在语义逻辑:ROW_NUMBER() 是纯序号,RANK() 是“跳级并列”,DENSE_RANK() 是“紧凑并列”。选错会导致业务排名结果出错,比如奖金发放规则依赖是否跳名次。
-
RANK() OVER (PARTITION BY dept ORDER BY score DESC)→ 相同 score 得相同名次,后续名次跳过已占位数 -
DENSE_RANK() OVER (PARTITION BY dept ORDER BY score DESC)→ 相同 score 得相同名次,后续名次紧接上一名 - 例如 score = [95,95,87] →
RANK得 [1,1,3],DENSE_RANK得 [1,1,2]
WHERE 不能直接过滤窗口函数结果,得用子查询或 CTE
想查“每个部门排名前 3 的员工”,不能写 WHERE rn ,因为窗口函数在 <code>WHERE 之后执行。直接写会报错 column "rn" does not exist。
必须把带窗口函数的查询包一层——要么用子查询,要么用 WITH CTE。否则逻辑写对了也跑不通。
- CTE 写法更清晰:
WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY score DESC) AS rn FROM employees ) SELECT name, dept, score FROM ranked WHERE rn
- 子查询写法等价但嵌套深:
SELECT * FROM (SELECT ..., ROW_NUMBER() OVER (...) AS rn FROM ...) t WHERE rn - 注意:
ORDER BY在窗口定义里控制排名顺序,外部ORDER BY控制最终结果顺序,两者独立
性能和索引建议:分组字段 + 排序字段联合索引最有效
当数据量大时,PARTITION BY dept ORDER BY score DESC 这类操作容易变慢。数据库无法直接走索引完成窗口计算,除非有合适索引支撑。
单列索引效果有限;真正起作用的是包含分组列和排序列的联合索引,且顺序要匹配窗口定义中的先后关系。
- 建索引优先考虑:
CREATE INDEX idx_dept_score ON employees(dept, score DESC); - 如果常按
score ASC排,就别加DESC,否则索引可能不被使用 - PostgreSQL 支持表达式索引,可应对复杂排序;MySQL 8.0 要求索引列顺序与
PARTITION BY+ORDER BY完全一致才高效
窗口函数本身不难,但 PARTITION BY 的边界意识、并列语义选择、执行顺序误解、索引适配这四点,实际写错频率极高。尤其多人协作时,有人默认用 ROW_NUMBER(),有人习惯 DENSE_RANK(),不显式注释清楚很容易埋坑。











