3

我有一个排行榜,其中有一个“分数”列。我还在 score 列上有一个索引,它允许我非常快速地按分数排序表格,甚至可以切出前十名或任意数量的分数 - 即使有 500 万行!

但是,我真正想要的是在排行榜中也显示玩家“排名”。我尝试了几种方法,但在处理超过 100000 行的任何内容时似乎都没有叠加(它们开始占用半秒以上,考虑到我期待数百个当前用户,这太长了)。

我目前正在使用以下查询来确定一个用户的排名,然后在我的输出中为高于此的用户简单地递增 - 但它非常慢。

SELECT  tro.score, tro.userId,  
(
    SELECT  count(*) 
    FROM    scoreboard tri
    WHERE   tri.score >= :score
) AS rank
FROM test_tracks tro
WHERE userId = :userID");

我正在考虑生成一个排名表来“缓存”用户在将新分数插入记分牌时的排名,但即使生成这个也需要很长时间(几分钟,可能是几小时)。

有谁知道建立排名的任何好的指南或技巧?最好我也希望能够对结果进行分页,但即使只是暂时建立排名就足够了!

提前致谢!

4

3 回答 3

5

最好的方法是使用一个定期更新的表(一个小时一次,一天一次等),这个表可能需要更长的查询并运行它。没有任何必要一直使用这些繁重的查询来破坏数据库。这也意味着长时间运行的查询会运行一次,而不是针对浏览该页面的每个用户。

SO上有一个完美的例子,每周/每月/每年的总数每天更新一次。

于 2012-08-10T08:14:27.963 回答
1

我能建议的最好的方法如下。(它适用于我的 500 万行,但它没有经过并发测试。由于某种原因,它在 InnoDB 中真的很慢,但在 MyISAM 表中工作亚秒级,因此会丢失事务支持。)

我创建了一个名为 ranks 的表:ranks( score int PK, num int ) 然后用这个作为种子: insert into ranks(score,num) select score,count(*) from player group by score;

之后我创建了这个存储过程:

DELIMITER $$
CREATE PROCEDURE testranks(
     OldScore INT 
    ,NewScore INT 
)
BEGIN
    INSERT INTO ranks (score,num) VALUES (NewScore,1) 
    ON DUPLICATE KEY 
    UPDATE num = num + 1;
    UPDATE ranks SET num = num - 1 WHERE score = OldScore;
    DELETE FROM ranks WHERE num = 0 AND score = OldScore;
    SELECT COUNT(*) FROM ranks WHERE score >= newscore;

END$$
DELIMITER ;

如果您调用 TestRanks(带有旧分数和新分数),它将返回新排名。

我很想看看其他人提出了什么解决方案。

于 2012-08-10T15:23:05.377 回答
-1

我最近对分数进行了排名。我发布了粗略的 mysql 代码,它应该可以进一步指导你。

SET @rank = 0, @prev_val = NULL;
SELECT id,rank, score FROM (
SELECT @rank := IF(@prev_val=score,@rank,@rank+1) AS rank,
@prev_val := score AS score, id
FROM rank ORDER BY score DESC
)AS result

其中表名是“rank”,字段是 id、score

希望这可以帮助

于 2012-08-10T08:20:20.133 回答