SQL中常用的窗口函数(排序函数)-row_number/r...
对比 SQL 窗口排序函数 row_number、rank、dense_rank、ntile 的返回规则、并列名次差异、适用场景和常见误区,帮助编写稳定的排名类查询。
SQL 中常用的窗口函数(排序函数)包括 row_number、rank、dense_rank 和 ntile。它们都需要配合 over 子句使用,可以在不折叠明细行的情况下,为每条记录生成排序编号或分组编号,常用于排名、取 TopN、分桶分析等场景。
基本语法
窗口排序函数的一般写法如下:
SELECT user_id,
score,
ROW_NUMBER() OVER (PARTITION BY class_id ORDER BY score DESC) AS rn
FROM exam_score;partition by 表示按哪个维度分组排名,order by 表示组内排序规则。如果省略分区,函数会在整个结果集上计算。
row_number:连续且不重复
row_number() 会为排序后的每一行生成唯一编号,即使排序字段值相同,也会给出不同序号。例如同分学生仍会得到 1、2、3 这样的连续编号。它最常用于“每组取第一条”“按更新时间保留最新记录”等场景。
为了结果稳定,order by 最好包含唯一或足够确定的字段。例如只按分数排序时,同分记录的先后顺序可能不稳定,可以再追加 id 或创建时间作为次级排序。
rank:并列后跳号
rank() 会给相同排序值相同名次,但后续名次会跳号。例如分数为 100、95、95、90,对应排名可能是 1、2、2、4。它适合体育竞赛、榜单排名等强调并列名次且保留名次间隔的场景。
如果业务要求“并列第二后下一名是第三”,则不应使用 rank,而应考虑 dense_rank。
dense_rank:并列后不跳号
dense_rank() 同样支持并列排名,但后续名次不会跳号。上面的例子会得到 1、2、2、3。它适合统计不同档位、连续等级或需要紧凑排名的分析场景。
在数据报表中,dense_rank 比 rank 更适合展示“第几个不同分值层级”,而 rank 更接近传统竞赛名次。
ntile:平均分桶
ntile(n) 用于把排序后的结果尽量平均分成 n 组,并返回每行所在的桶编号。它常用于用户分层、收入分位、成绩分组等分析。
SELECT user_id,
amount,
NTILE(4) OVER (ORDER BY amount DESC) AS quartile
FROM user_amount;需要注意,ntile 是按行数分桶,不一定保证每个桶的指标总和接近。如果要按金额累计占比分层,需要使用累计窗口函数另行计算。
选择建议
需要唯一序号时用 row_number;需要并列且跳号时用 rank;需要并列但连续排名时用 dense_rank;需要平均分组时用 ntile。实际编写 SQL 时,排序字段的确定性、空值处理和分区口径往往比函数名本身更重要。


