选rank()、dense_rank()、row_number()关键看并列处理:rank()并列占位(1,2,2,4),适合强调段位;dense_rank()并列不占位(1,2,2,3),适合梯队划分;row_number()强制唯一(1,2,3,4),仅用于需绝对顺序场景。

rank()、dense_rank()、row_number() 三者到底怎么选
选错函数直接导致业务排名结果出错,比如活动榜单里并列第2后跳到第4,用户立刻质疑数据不准。核心区别在“并列如何占位”:
-
rank():并列则占位,1, 2, 2, 4 —— 适合强调名次“段位感”,如赛事排行榜 -
dense_rank():并列不占位,1, 2, 2, 3 —— 适合梯队划分,如按销售额分A/B/C级客户 -
row_number():强制唯一,1, 2, 3, 4 —— 仅用于需要绝对顺序的场景,如分页取第N条、去重抽样
别硬背口诀,看实际输出:对同一组数据跑三者,对比结果最直观。PostgreSQL 和 MySQL 8.0+ 行为一致,但 Hive 旧版不支持 dense_rank(),得用自连接模拟。
ORDER BY 里漏写 NULLS LAST 就会崩
只要排序字段可能为 NULL,不显式声明空值位置,不同数据库默认行为天差地别。PostgreSQL 默认 NULLS FIRST,MySQL 8.0 默认 NULLS LAST,Oracle 更是随版本变。结果就是本地测通,上线后排名全乱。
- 升序排名时,加
ORDER BY score DESC NULLS LAST,确保NULL排最后(通常代表未参与,不该占高位) - 降序排名时,用
ORDER BY score ASC NULLS LAST,避免NULL挤进前几名 - 别依赖数据库默认,所有窗口函数的
ORDER BY子句必须带NULLS FIRST/LAST
实操建议:在开发环境故意插入几条 NULL 数据跑一遍,比查文档管用。
WHERE 不能直接过滤窗口函数结果,得套一层子查询
新手常写 SELECT *, rank() OVER (...) rnk FROM t WHERE rnk ,报错 <code>column "rnk" does not exist。因为 SQL 执行顺序是 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY,窗口函数在 SELECT 阶段才计算,WHERE 根本看不见它。
- 正确做法是用子查询或 CTE:
WITH ranked AS ( SELECT *, rank() OVER (ORDER BY score DESC NULLS LAST) rnk FROM users ) SELECT * FROM ranked WHERE rnk <= 10;
- MySQL 8.0+ 和 PostgreSQL 支持 CTE,Hive 3.1+ 也行;老 Hive 只能用嵌套
SELECT - 别图省事在应用层做筛选,大数据量下网络和内存浪费严重
分区键写错导致“全局排名”变“每人排自己”
PARTITION BY 不是可选项,是决定计算范围的关键。漏写或写错字段,排名就从“全公司销售TOP10”退化成“每人对自己历史记录排名”,逻辑完全错位。
- 查每个部门的销售前三:
PARTITION BY dept_id ORDER BY amount DESC - 查每个用户最近3笔订单的时间顺序:
PARTITION BY user_id ORDER BY created_at DESC - 如果业务要求“不分组”,就别写
PARTITION BY,整个结果集就是一个分区 - Hive 中
PARTITION BY字段必须出现在SELECT列表或GROUP BY中,否则报错;PostgreSQL 更宽松,但逻辑易混淆
重构时最容易忽略的是隐式分区需求——比如原逻辑靠应用层分组循环调用 SQL,改窗口函数后必须把那个分组维度显式落到 PARTITION BY 上,否则就漏数据。

















