如何使用ROW_NUMBER、RANK或DENSE_RANK实现同分玩家共享名次的玩家积分榜(含位次跳过规则)
如何使用ROW_NUMBER、RANK或DENSE_RANK实现同分玩家共享名次的玩家积分榜(含位次跳过规则)
嘿,这个需求我太熟了!正好可以用SQL里的窗口函数来解决,尤其是RANK()完全匹配你要的位次规则——同分玩家共享名次,且下一个位次会跳过中间的数字(比如两个第1之后直接到第3)。我来给你一步步拆解实现方法:
第一步:先确认数据结构
你已经给出了玩家表和成绩表的创建及插入语句,我帮你格式化好:
CREATE TABLE players ( id INT PRIMARY KEY, name VARCHAR(50) ); CREATE TABLE results ( id INT PRIMARY KEY, player_id INT, score INT, FOREIGN KEY (player_id) REFERENCES players(id) ); INSERT INTO players (id,name) VALUES (1, 'Uolevi'), (2,'Maija'), (3,'Liisa'), (4,'Kaaleppi'), (5,'Kotivalo'); INSERT INTO results (id, player_id, score) VALUES (1, 1, 100), (2, 2, 200), (3, 3, 200), (4, 4, 100), (5, 5, 50);
第二步:实现符合要求的查询语句
直接用RANK()窗口函数就能搞定,同时要注意同分玩家按姓名字母顺序排列,完整查询如下:
SELECT RANK() OVER (ORDER BY r.score DESC, p.name ASC) AS "Order", p.name AS "Name", r.score AS "Score" FROM players p JOIN results r ON p.id = r.player_id ORDER BY "Order", "Name";
第三步:解释关键逻辑
RANK() OVER (ORDER BY r.score DESC, p.name ASC):这是核心!这个窗口函数会先按分数从高到低排序,分数相同的情况下按姓名升序排列。同分的玩家会获得相同的位次,而且会自动跳过中间的数字——比如有2个玩家是第1名,下一个玩家直接是第3名,完全符合你的要求。- JOIN关联表:通过
player_id把玩家表和成绩表关联起来,这样就能同时获取玩家姓名和对应的分数。 - 最终排序:最后的
ORDER BY "Order", "Name"确保结果按位次和姓名顺序输出,和你预期的格式完全一致。
对比三个常用窗口函数的区别(帮你理清为什么选RANK)
为了让你更清楚,我顺便对比一下另外两个常用的排名窗口函数:
ROW_NUMBER():不管分数是否相同,都会给每一行分配唯一的连续位次。比如两个200分的玩家会被标记为1和2,不符合你共享名次的需求。RANK():就是我们用的这个,同分共享位次,且跳过中间的数字,完美匹配你的规则。DENSE_RANK():同分共享位次,但下一个位次是连续的(比如两个1之后是2),不符合你要跳过位次的要求。
查询结果(和你预期完全一致)
| Order | Name | Score |
|---|---|---|
| 1 | Liisa | 200 |
| 1 | Maija | 200 |
| 3 | Kaaleppi | 100 |
| 3 | Uolevi | 100 |
| 5 | Kotivalo | 50 |
备注:内容来源于stack exchange,提问作者FC Chelsea
相关产品推荐
相关产品推荐

