ROW_NUMBER()、RANK()和DENSE_RANK()结果不同是因为处理并列方式不同:ROW_NUMBER()严格递增不重复;RANK()并列后跳号;DENSE_RANK()并列不跳号;选择取决于业务对“并列是否占位”的定义。

为什么 ROW_NUMBER()、RANK() 和 DENSE_RANK() 结果不一样
三者都生成序号,但处理并列(相同排序值)的方式不同:ROW_NUMBER() 严格递增,哪怕值相等也强制分配不同序号;RANK() 遇到并列会跳过后续编号(比如两个第1名后直接是第3名);DENSE_RANK() 并列不跳号(两个第1名后是第2名)。选哪个取决于业务对“并列是否占位”的定义。
常见错误是默认用 ROW_NUMBER() 做成绩排名,结果同分学生排在不同名次,引发投诉。
- 成绩并列需体现公平性 → 用
DENSE_RANK() - 需要唯一行标识(如分页取第10–20条)→ 用
ROW_NUMBER() - 强调“击败了多少人”(含并列者)→ 用
RANK()
ORDER BY 必须写在 OVER() 里,不能靠外层 ORDER BY
窗口函数的排序逻辑完全由 OVER(ORDER BY ...) 决定,外层 ORDER BY 只影响最终结果集显示顺序,不影响排名计算。很多人把排序写在外层,发现排名乱序,其实是没生效。
示例:按销售额降序排名,但想最后按姓名升序展示
SELECT name, sales, RANK() OVER (ORDER BY sales DESC) AS rank_by_sales FROM sales_data ORDER BY name ASC; -- 这个只影响输出顺序,不影响 RANK() 计算
-
OVER()中的ORDER BY是排名依据,不可省略 - 若需多级排序(如先按部门,再按销售额),写成
OVER(PARTITION BY dept ORDER BY sales DESC) - 字符串、日期字段排序要注意 NULL 处理,默认
NULLS LAST或NULLS FIRST因数据库而异(PostgreSQL 支持显式声明,MySQL 不支持)
用 PARTITION BY 实现分组内独立排名
比如“每个部门销售额 Top 3”,必须用 PARTITION BY dept 把数据按部门切片,否则算的是全公司统一排名。
使用模板与程序分离的方式构建,依靠专门设计的数据库操作类实现数据库存取,具有专有错误处理模块,通过 Email 实时报告数据库错误,除具有满足购物需要的全部功能外,成新商城购物系统还对购物系统体系做了丰富的扩展,全新设计的搜索功能,自定义成新商城购物系统代码功能代码已经全面优化,杜绝SQL注入漏洞前台测试用户名:admin密码:admin888后台管理员名:admin密码:admin888
注意 PARTITION BY 的粒度直接影响结果——漏写或写错字段会导致聚合范围错误,例如把 region 写成 region_id 而字段名实际是 region_code,可能全为 NULL 分区,所有行排在同一组。
- 分区字段必须存在于 SELECT 或 GROUP BY 中(取决于是否聚合)
- 多个分区键用逗号分隔:
PARTITION BY dept, year - 分区后各组独立排序,组间序号不连续(每组都从 1 开始)
- MySQL 8.0+、PostgreSQL、SQL Server 支持;旧版 MySQL(5.7 及之前)不支持窗口函数,需用变量模拟,稳定性差
性能隐患:大表上 ORDER BY + PARTITION BY 可能触发临时表和文件排序
当数据量超过内存 sort_buffer_size,数据库会写磁盘临时文件,速度骤降。尤其 PARTITION BY 字段基数高(如用户 ID)、且 ORDER BY 字段无索引时,问题最明显。
- 优先为
PARTITION BY + ORDER BY组合建联合索引,例如(dept, sales) - 避免在
OVER()中用表达式排序(如ORDER BY UPPER(name)),无法走索引 - PostgreSQL 可用
EXPLAIN ANALYZE观察是否出现Sort Method: external merge - 如果只要 Top N,考虑用
LIMIT配合子查询替代全量排名,减少计算量
真正难的不是写出语法,而是理解分区边界和排序依赖如何映射到业务场景——比如“活跃用户周榜”要按自然周分区,但周起始日设置错一天,整张榜就偏移了。









