【问题标题】:Calculating rank in PHP/MySQL在 PHP/MySQL 中计算排名
【发布时间】:2011-06-18 05:36:27
【问题描述】:

我在 MySQL 中有一个表,比如说它有两个字段 Username、GameName 和 Score。 我想计算个人游戏名称的用户排名,以便进行查询

SELECT * FROM scores WHERE `GameName` = 'Snake' ORDER BY `Score` DESC

按从高到低的顺序获取所有用户的列表,并为每个用户分配一个编号。

但是有没有更简单的方法来获得单个用户的排名,而不是选择整个表格,因为这似乎不太有效。

谢谢

【问题讨论】:

  • 我的想法是,如果您可以在 MySQL 中执行此操作,那将是通过使用临时表、子选择和/或使用 Rank 别名计算行号。诀窍是让 MySQL 填充一个临时表/子查询,然后只允许您选择一个结果(单个玩家的最高排名)。如果没有反复试验和测试,那将是我不确定该怎么做。见:jimlife.wordpress.com/2008/09/09/…
  • 我确实理解您的意思,您正在寻找用户在所有玩家中的总体最高分排名,而不是该玩家的最高分,对吧?

标签: php mysql ranking


【解决方案1】:

如果您想要整体排名,很遗憾您必须对整个表格进行排序。简而言之,如果不知道表中的其他排名,就无法知道某人在表中的排名。

也就是说,如果您担心性能,这里有一个相当简单的解决方案 - 缓存您的排名查询结果(可能到另一个 MySQL 表中!),并查询所有读取。当有人发布新分数时,重新计算您的临时表。您可以定期刷新某个排名以下的所有记录(例如,排名低于 100 的任何人都会从分数表中删除)以保持快速重新计算,因为没有人会在被更高的分数击倒后提升排名。

# Create your overall leaderboards once
create table leaderboards (rank integer primary key, score_id integer, game varchar(65), user_id integer, index game_user_id_idx (game, user_id))


# To refresh your leaderboard, we'll query the ranks for the game into a temporary table, flush old records from scores, then copy
# the new ranked table into your leaderboards table.
# We'll use MySQL's CREATE TABLE...SELECT syntax to select our resultset into it directly upon creation.
create temporary table tmp_leaderboard (rank integer primary key auto_increment, score_id integer, game varchar(65), user_id integer)
  select ID, GameName, UserID, from scores where GameName = '$game' order by score desc;

# Remove old rankings from the overall leaderboards, then copy the results of the temp table into it.
delete from leaderboards where game = '$game';
insert into leaderboards (rank, score_id, game, user_id)
  select rank, score_id, game, user_id from tmp_leaderboard;

# And then clean up the lower scores from the Scores table
delete from scores join tmp_leaderboard on scores.id = tmp_leaderboard.score_id, scores.GameName = tmp_leaderboard.game where tmp_leaderboard.rank < 100;

# And we're done with our temp table
drop table tmp_leaderboard;

然后,每当您想读取游戏的排名时:

select rank from leaderboards where game = '$game' and user_id = '$user_id';

【讨论】:

  • 我知道这是可能的,我只是不知道具体如何。这真的会比仅将查询限制为需要的内容(前 100 名)并让 PHP 迭代更快吗?我从来不需要临时表,所以我不确定。另外,这可能是一个存储过程,对吧?
  • 如果你保留从分数表中刷新旧分数的部分,它可能会非常快。只要您在gamescore 上有索引,PHP 解决方案的速度可能可以接受,但它肯定会更慢,因为您将重新计算排名并在每次读取时从该表中读取 100 行,而不是每次读取 1 个表中的 1 行。这很容易成为一个存储过程,如果你想走这条路,你可以重写它以跳过选择进入临时表的步骤。
  • 另外,您在最后一个查询中得到了排行榜而不是排行榜。
【解决方案2】:

您无法避免读取表中的大量数据 - 但您无需将其一直拖回处理脚本:

SELECT COUNT(*)
FROM scores 
WHERE `GameName` = 'Snake'
AND user=$some_user;

(由于您可能希望第一个人的排名为“1”而不是“0”,因此增加结果)。

但是,如果您需要经常运行查询,则值得维护排序结果的具体化视图。

【讨论】:

  • 美容理念。干净而美丽。
【解决方案3】:

从您的用户表中获取用户 ID 并在您的查询中使用它


SELECT * FROM scores WHERE `GameName` = 'Snake' 
and `youruseridfield` = '$useridvalue'
ORDER BY `Score` DESC

【讨论】:

  • 确保$useridvalue被转义。
  • 不能在 WHERE 语句之前使用 ORDER
【解决方案4】:

SELECT * FROM scores WHERE 'GameName' = 'Snake' &amp;&amp; userID = '$userID' ORDER BY 'Score' DESC

【讨论】:

  • 这不会让您获得排名,因为它只会在结果被where 子句缩减后对结果进行排序。他想要整体排名 - 排行榜。
【解决方案5】:

看看有没有办法在 MySQL 中获得排名,但这里是你可以在 PHP 中做到的方法:

function getRank($user, $game, $limit=50) {
    $sql = "
SELECT @rank:=@rank+1 AS Rank, User, GameName
FROM scores, (SELECT @rank:=1) AS i
WHERE `GameName` = '$game' 
ORDER BY `Score` DESC
LIMIT 0, $limit
";

    $result =  mysql_query($sql);

    while ($row = mysql_fetch_assoc($result)) {
        if ($row['User'] == $user) {
            return $row['Rank'];
        }
    }

    return -1;
}

注意,我把限制放在那里,否则你只会得到 30 个结果。如果玩家未排名,则返回 -1。

【讨论】:

    【解决方案6】:

    我的一个查询解决方案:

    select @rank:=@rank+1 AS Rank,L1.* from 
        (select @rank:=0) as L2,
        (select i.*,count(*) as sum 
            FROM 
            transactions t
            LEFT JOIN companies c on (c.id = t.company_id)  
            LEFT JOIN company_industries ci on (c.id = ci.company_id)  
            LEFT JOIN industries i on (ci.industry_id = i.id)
            GROUP by i.name 
            ORDER BY sum desc ) as L1;
    

    【讨论】:

      【解决方案7】:

      您应该为 GameName 和 Score 添加索引。 (并且不要忘记在插入查询之前转义 GameName => mysql_real_escape_string)。 @Bryan:不应该是“AND”而不是“&&”吗?

      【讨论】:

      • 我认为 && 没有问题。我在我的 php.ini 中使用它。 stackoverflow.com/questions/2803321/and-vs-as-operator
      • MySQL 中的 && 没有任何问题,它们都是有效的并且可以作为彼此的别名。许多人按照惯例在 MySQL 中使用 AND,但这并不是一成不变的。 dev.mysql.com/doc/refman/5.0/en/…
      • 另外,这并不能回答问题,它只是为了加快查找本身的速度。它更多地属于对原始问题的评论。
      【解决方案8】:

      下面的查询使用了rank函数,这也可以实现:

      WITH game_rank AS(
          SELECT 
              user_id, 
              gameName, 
              RANK() OVER (
                  PARTITION BY gameName
                  ORDER BY score DESC
              ) order_value_rank
          FROM
              scores
      )
      SELECT 
          * 
      FROM 
          game_rank
      WHERE 
          gameName = "Snake";
      

      【讨论】:

        猜你喜欢
        • 2015-04-15
        • 2010-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-28
        • 2012-09-14
        • 2011-01-23
        相关资源
        最近更新 更多