ROW_NUMBER() 必须配合 PARTITION BY 才能按年分区排名;若仅写 OVER (ORDER BY score DESC) 会全校混排,而 PARTITION BY year 可使排名在各年度内独立重置,MySQL 5.7 等旧版本不支持需用变量模拟。
ROW_NUMBER() 必须配合 PARTITION BY 才能按年分区排名
直接写
会把全校所有年级所有学生成绩混在一起排,根本不是“分年度”。关键在
——它定义了窗口的边界。只要把年份字段(比如
或
)放进
,就能让排名在每个年份内独立重置。
常见错误是漏写
,或者误用
(窗口函数不走 GROUP BY 流程)。
年份字段必须是明确的列,如
;若只有入学日期
,得先用
或
提取
排序字段建议加上
,避免同分时结果不稳定(不同数据库可能返回不同顺序)
PostgreSQL 和 SQL Server 支持
,MySQL 8.0+ 推荐用
,旧版 MySQL 可用
MySQL 5.7 不支持窗口函数?得换思路
MySQL 5.7 及更早版本不识别
,强行运行会报错
。这时不能硬套语法,得用变量模拟:
注意:这种写法依赖
严格生效,且不能嵌套在子查询里再加过滤(比如
更稳。
排名并列怎么处理?RANK() 和 DENSE_RANK() 的区别不能忽略
如果同一年有多个学生同分,
会强制分配不同名次(比如 95 分占第1,另一个 95 分只能是第2),而实际业务常要“同分同名次、跳过后续”——这就该用
:
→ 1,2,3,4…(无并列)
→ 1,1,3,4…(同分同名,跳2)
→ 1,1,2,3…(同分同名,不跳)
选哪个取决于业务规则。教务系统公示排名通常用
,而内部绩效统计可能倾向
。
性能隐患:没加索引的 ORDER BY 会让排名变慢
当数据量超过几万行,
会触发全表扫描+临时文件排序,响应明显变卡。最有效的优化是建联合索引:
推荐索引:
如果查询还常带
过滤,可扩展为
,覆盖查询避免回表
注意:MySQL 中
在索引定义里仅从 8.0 开始真正生效;5.7 实际按升序存储,但优化器仍能利用索引加速
没索引时,10 万行数据排名可能耗时数秒;加了合适索引后通常压到 50ms 内。这个点容易被忽略,尤其测试库数据少看不出来问题。
ROW_NUMBER() OVER (ORDER BY score DESC)PARTITION BYyearSUBSTRING(stu_id, 1, 4)PARTITION BYPARTITION BYGROUP BYenroll_yearenroll_dateYEAR(enroll_date)EXTRACT(YEAR FROM enroll_date)ORDER BY score DESC, stu_id ASCYEAR()EXTRACT(YEAR FROM enroll_date)LEFT(enroll_date, 4)ROW_NUMBER()ERROR 1305 (42000): FUNCTION xxx.ROW_NUMBER does not existSELECT year, stu_id, score,
@rank := IF(@prev_year = year, @rank + 1, 1) AS rank,
@prev_year := year
FROM scores
CROSS JOIN (SELECT @rank := 0, @prev_year := '') AS _
ORDER BY year, score DESC, stu_id;ORDER BYWHERE rank ),否则变量逻辑会乱。MySQL 8.0+ 直接升级用原生 ROW_NUMBER()ROW_NUMBER()RANK()ROW_NUMBER() OVER (PARTITION BY year ORDER BY score DESC)RANK() OVER (PARTITION BY year ORDER BY score DESC)DENSE_RANK() OVER (PARTITION BY year ORDER BY score DESC)RANK()DENSE_RANK()ROW_NUMBER() OVER (PARTITION BY year ORDER BY score DESC)CREATE INDEX idx_year_score ON scores(year, score DESC);stu_id(year, score DESC, stu_id)DESCORDER BY ... DESC