导读:本期聚焦于广州SEO公司创作的《SQLite窗口函数RANK与DENSE_RANK有什么区别?排名场景如何正确选择》,敬请观看详情。同样是排名,为什么RANK和DENSE_RANK给出的结果不一样?当数据中存在并列名次时,RANK会跳过后续名次,比如两个人并列第一后下一个人直接是第三名,而DENSE_RANK则连续编号,下一个人是第二名。本文围绕SQLite窗口函数,详细讲解这两个函数的语法结构、排序规则和底层计算逻辑,通过学生成绩、销售业绩等典型场景的SQL示例对比两者的输出差异,并分析ROW_NUMBER在其中的角色,帮助你根据业务需求判断该用哪个函数,避免排名统计出错。

排名统计是数据分析中非常常见的需求,比如给学生成绩排名、给销售业绩排序、给商品销量做榜单。SQLite从3.25版本开始支持窗口函数,其中RANK()DENSE_RANK()是两个最容易混淆的函数。它们都能处理并列名次的问题,但处理方式完全不同:一个会跳号,一个不跳号。如果选错了函数,排行榜的名次就会出现间断或者重复,直接影响业务结果的正确性。本文结合具体SQL示例,把这两个函数的区别讲清楚。

SQLite窗口函数RANK与DENSE_RANK有什么区别?排名场景如何正确选择

一、先看两个函数的基本语法和核心区别

窗口函数的标准语法是在函数名后面跟上OVER子句,用来定义分区和排序规则。SQLite中写法如下:

SELECT
    name,
    score,
    RANK() OVER (ORDER BY score DESC) AS rank_num,
    DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank_num
FROM students;

假设表中有这样一组数据:张三95分、李四90分、王五90分、赵六85分。执行上面的查询,结果会是:张三的RANK是1、DENSE_RANK也是1;李四和王五并列,RANK都是2、DENSE_RANK也都是2;到了赵六这里区别就出来了,他的RANK是4,而DENSE_RANK是3。

这就是两者的核心区别:RANK在遇到并列名次时会跳过被占用的名次。李四和王五占了第2名和第3名两个位置,所以下一个人直接从4开始,名次是不连续的,但总人数和最高名次是对得上的。而DENSE_RANK是密集排名,并列的人只占用一个名次,不管多少人并列第二,下一个人的名次永远是3,名次序列连续没有空洞,但最大名次数可能小于总记录数。

可以这样记:RANK像体育比赛的并列规则,两人并列亚军就没有季军;DENSE_RANK像档次划分,并列的人属于同一档,下一档紧跟着编号。

二、为什么会有这种差异:窗口函数的计算逻辑

理解差异的根源,需要知道这两个函数在OVER子句定义的窗口内是怎么计数的。RANK的计算规则是:当前行的排名等于窗口内排在它前面的行数加1。李四和王五的分数都是90,按分数降序排,排在90分前面的只有张三一行,所以两人RANK都是2。赵六前面有三行(张三、李四、王五),所以RANK是4。并列的行共享同一个排名,但它们各自都占了一个位置。

DENSE_RANK的计算规则则是:当前行的排名等于排在它前面的不同取值个数加1。在赵六之前,分数只有95和90两个不同值,所以DENSE_RANK是3。并列的行不管有多少人,都被压缩成一个取值来计数。

顺便提一下第三个常被一起讨论的函数ROW_NUMBER(),它最简单,就是给窗口内的行顺序编号,即使分数相同也会得到1、2、3、4这样互不相同的序号,完全没有并列的概念。三个函数放在一起看,处理并列的方式分别是:ROW_NUMBER不承认并列、RANK承认并列但跳号、DENSE_RANK承认并列且不跳号。下面的示例把三者对比得很直观:

SELECT
    name,
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num,
    RANK()       OVER (ORDER BY score DESC) AS rnk,
    DENSE_RANK() OVER (ORDER BY score DESC) AS drnk
FROM students
ORDER BY score DESC;

查询结果中,两名90分的学生row_num分别是2和3,rnk都是2,drnk也都是2,而85分的赵六三种结果分别是4、4、3,一眼就能看出差别。

三、结合PARTITION BY实现分组内排名

实际业务中很少只做全表排名,更多是分组内排名,比如每个班级内部按成绩排名、每个月内部按销量排名。这时需要在OVER子句中加上PARTITION BY,它会把数据按指定字段切分成多个分区,每个分区独立计算排名,互不干扰。

SELECT
    class,
    name,
    score,
    RANK() OVER (PARTITION BY class ORDER BY score DESC) AS rnk,
    DENSE_RANK() OVER (PARTITION BY class ORDER BY score DESC) AS drnk
FROM students;

这条语句会对每个班级分别计算RANK和DENSE_RANK,每个班的排名都从1开始。PARTITION BY不影响两个函数本身的差异,只是把计算范围缩小到了分组内部。需要注意的是,最终输出结果的顺序不一定按排名排,如果希望结果按班级和名次展示,最好在外面再套一层ORDER BY,或者直接调整OVER子句里的排序字段。

还有一个常见用法是配合子查询筛选前几名。比如取每个班级的前三名,如果用ROW_NUMBER,并列第三的学生只会保留一个,可能不符合预期;如果用DENSE_RANK筛选WHERE drnk <= 3,并列的学生会全部保留,甚至可能超过三个人。用RANK筛选则可能出现结果不足三人的情况。这个细节在取Top N时特别容易踩坑,选择哪个函数完全取决于业务上对并列名次的处理要求。

四、如何根据业务场景选择正确的函数

选择的关键问题是:并列名次之后,下一个名次应该是几。如果业务要求名次能反映真实的竞争位置,比如竞赛评奖、只取固定名额的选拔,用RANK更合适。例如比赛只取前三名发奖,两人并列第二时,按照RANK规则第三名就空缺了,这符合大多数竞赛的评奖逻辑,用WHERE rnk <= 3筛选正好取到该拿奖的人。

如果业务要求名次连续、用来表示等级或档次,比如绩效评级分档、会员等级划分、销售提成档位,用DENSE_RANK更合适。举个例子,提成规则是第一名拿一档提成、第二名拿二档提成,如果用RANK,两人并列第一后下一个直接是第三名,二档提成永远没人拿,这时候DENSE_RANK才能保证档位连续覆盖。

还有一些场景需要注意细节。比如统计某个名次上有多少人,用RANK配合计数会很方便,因为名次值和总人数的关系是确定的;而在分页展示排行榜时,DENSE_RANK的名次连续性对前端展示更友好,不会出现用户翻页时看到名次从2直接跳到5的困惑。如果只是需要给结果集编个号做标识,比如配合LIMIT做随机抽样编号,那用ROW_NUMBER就够了,没必要引入排名语义。

最后提醒一点,SQLite对窗口函数的支持从3.25.0版本才开始,如果在使用旧版本的SQLite(比如某些老版本的移动端嵌入式环境),这些函数会直接报语法错误,升级SQLite版本是唯一的解决办法。写SQL时建议像上面的例子一样,给排名列起清晰的别名,并把两个函数放在一起对比输出结果,确认名次逻辑符合业务预期后再定型,这样能避免大部分排名统计的错误。

SQLite窗口函数RANK函数DENSE_RANK修改时间:2026-09-15 13:52:37

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260915/57310.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。