我写了一个愚蠢的小游戏,并希望拥有某种排行榜网站。
通常排行榜仅限于10或20名顶级球员,但我认为如果能够为每位球员录制,他们的最高分会更好。然后,我总能展示他们的世界级别。
一个简单的架构,例如:
create table leaderboard (
userid varchar(128) not null,
score real not null,
when datetime not null
);
create index on leaderboard(userid);
将存储我需要的最少量信息 - 每个用户获得最佳分数1个条目。
我的问题围绕如何有效地确定某人在排行榜上的位置。一般的想法是,我希望他们在列表中的位置返回:
select userid from leaderboard order by score desc
但从数据库性能的角度来看,运行此查询然后线性搜索列表似乎有点荒谬。即使这样,我也很难想象一个可以快速操作的查询/模式。
有什么想法吗?
(我更希望保持数据库架构和查询通用(不依赖于供应商)。但是,如果一个供应商使这很容易,我很乐意使用MS SQL或MySQL。
答案 0 :(得分:13)
怎么样:
select count(*)+1 as rank from leaderboard
where score > (select score from leaderboard where userid = ?)
您还需要分数列上的索引。
使用count()+1
执行score > (...)
即使多个玩家获得相同分数,也会为您提供准确的排名;使用count()
执行score >= (...)
不会。
答案 1 :(得分:2)
在SQL Server 2005及更高版本中,您可以使用RANK()
函数根据每个用户的分数返回排名
SELECT
userid,
RANK() OVER
(ORDER BY score) AS rank
FROM leaderboard
如果您有多种类型的“游戏类型”,那么您可以将其包含在排行榜表格中,并使用PARTITION BY
功能中的RANK
子句来确定每种游戏类型的排名。
答案 2 :(得分:1)
显而易见的选择是在“得分”上创建一个索引,这是完全合理的。 (听起来你想要保留两个值 - 累积分数和高水分 - 否则我会误解?)
除非您期望成千上万的用户,否则即使是表扫描也不应该是具有那么多记录的表上的大问题。
答案 3 :(得分:1)
看起来你想查询一下:
select userid , max(score)
from leaderboard
group by userid
order by max(score) desc
这会为每个用户返回一个带有1个条目的排行榜。
以下编辑: 我在评论中看到你想看到排名,而不是分数。 为此我不知道ANSI SQL答案,但具体数据:
在MySQL中:
SELECT @rownum:=@rownum+1 rank
, t.userid
FROM (SELECT @rownum:=0) r,
(select userid , score
from leaderboard
order by score desc
) t;
在Oracle中,您可以使用RANK语句。
答案 4 :(得分:1)
如果不必是实时的(例如,如果每天更新一次是可接受的),请添加一个额外的“位置”字段,并使用按分数排序的查询定期更新。
答案 5 :(得分:1)
在Sql server 2005中,Rank()几乎可以帮到你。但是,如果你拥有数百万条记录,那么无论何时基础统计数据发生变化,都会实时对其进行排名。
我尝试在select查询的基础上创建一个Indexed视图...(这个帖子中的投票答案)但是Sql 2005不会让我创建它因为你不能在索引视图中使用子查询,自引用。
所以我们的解决方法是使用Row()函数每晚更新排名表。为了避免在执行此更新时阻塞,我们保留了2个正在更新的排名表副本和一个正在应用程序中使用的副本。我们有一个RankingView,它指向任何给定时间的活跃排名表。
我想知道是否有针对真正大型表的实时排名更新的解决方案?
答案 6 :(得分:0)
我在考虑选择"选择计数(*)+ 1作为排行榜的排名 得分> (从用户ID =?的排行榜中选择分数)"
考虑以下情况: 球员1,得分100 球员2,得分100 球员3,得分50
使用该SQL,玩家3的等级将为3,其中正确答案应为2,因为玩家1和玩家2在第一个位置并列。在这种情况下,count()聚合函数考虑2个记分,其中Score列> 50。
为了处理这种情况,我认为正确的选择是制作一个分组,因此可重复的分数值将作为单个排名位置处理:
select score, count(*)+1 as rank from leaderboard
group by score having (score) > (select score from leaderboard where userid = ?)