如何组合这两个查询来计算排名变化?

Z0q*_*Z0q 7 php mysql sql mysqli combinations

介绍

我有一个使用排名的游戏的高分表.得分表表示当前的高分和玩家信息,并且最近的表表示用户的所有最近发布的分数,其可能是或可能不是新的最高分.

排名下降是通过计算玩家当前排名减去他们在达到最新最高分时的排名来计算的.

排名增加的计算方法是计算玩家在达到最新最高分时的排名减去他们达到之前最高分时的排名.

最后,如代码所示: $change = ($drop > 0 ? -$drop : $increase);


我使用以下两个查询结合一些PHP代码来计算排名变化.它工作得很好,但有时有点慢.

是否有办法优化或组合两个查询+ PHP代码?

我创建了第一个查询的SQL小提琴:http://sqlfiddle.com/#!9/30848/1

表格已经填充了内容,因此不应更改其结构.

这是当前的工作代码:

$q = "
            select
            (
            select
                coalesce(
                    (
                        select count(distinct b.username)
                        from recent b
                        where
                            b.istopscore = 1  AND
                            (
                                (
                                    b.score > a.score AND
                                    b.time <= a.time
                                ) OR
                                (
                                    b.score = a.score AND
                                    b.username != a.username AND
                                    b.time < a.time
                                )
                            )
                        ), 0) + 1 Rank
            from scores a
            where a.nickname = ?) as Rank,
            t.time,
            t.username,
            t.score
            from
            scores t
            WHERE t.nickname = ?
            ";

            $r_time = 0;

            if( $stmt = $mysqli->prepare( $q ) )
            {
                $stmt->bind_param( 'ss', $nick, $nick );
                $stmt->execute();
                $stmt->store_result();
                $stmt->bind_result( $r_rank, $r_time, $r_username, $r_score );

                $stmt->fetch();

                if( intval($r_rank) > 99999 )
                    $r_rank = 99999;

                $stmt->close();
            }

            // Previous Rank
            $r_prevrank = -1;

            if( $r_rank > -1 )
            {
                $q = "
                select
                    coalesce(
                        (
                            select count(distinct b.username)
                            from recent b
                            where
                                b.istopscore = 1  AND
                                (
                                    (
                                        b.score > a.score AND
                                        b.time <= a.time
                                    ) OR
                                    (
                                        b.score = a.score AND
                                        b.username != a.username AND
                                        b.time < a.time
                                    )
                                )
                            ), 0) + 1 Rank
                from recent a
                where a.username = ? and a.time < ? and a.score < ?
                order by score desc limit 1";

                if( $stmt = $mysqli->prepare( $q ) )
                {
                    $time_minus_one = ( $r_time - 1 );

                    $stmt->bind_param( 'sii', $r_username, $time_minus_one, $r_score );
                    $stmt->execute();
                    $stmt->store_result();
                    $stmt->bind_result( $r_prevrank );

                    $stmt->fetch();

                    if( intval($r_prevrank) > 99999 )
                        $r_prevrank = 99999;

                    $stmt->close();
                }
                $drop = ($current_rank - $r_rank);
                $drop = ($drop > 0 ? $drop : 0 );


                $increase = $r_prevrank - $r_rank;
                $increase = ($increase > 0 ? $increase : 0 );

                //$change = $increase - $drop;
                $change = ($drop > 0 ? -$drop : $increase);
            }

            return $change;
Run Code Online (Sandbox Code Playgroud)

Art*_*rth 3

如果您将当前的最高分数分离到一个新表中,而所有原始数据都在最近的分数中可用..您已经有效地生成了一个汇总表。

为什么不继续总结和总结所有需要的数据呢?

那么这只是一个你知道什么以及什么时候可以知道的情况:

  • 当前排名 - 取决于其他行
  • 新最高分的排名 - 可以计算为当前排名并在插入/更新时存储
  • 最高分的先前排名 - 当记录新的最高分时,可以从旧的“新最高分排名”转移。

我会更改您的分数表以包含两个新列:

  • 分数 - id、分数、用户名、昵称、时间、rank_on_update、old_rank_on_update

并在更新/插入每一行时调整这些列。看起来您已经有了可用于在第一次迭代中反向拟合此数据的查询。

现在您的查询变得更加简单

从分数中获取排名:

SELECT COUNT(*) + 1 rank
  FROM scores 
 WHERE score > :score
Run Code Online (Sandbox Code Playgroud)

来自用户名:

SELECT COUNT(*) + 1 rank
  FROM scores s1
  JOIN scores s2
    ON s2.score > s1.score
 WHERE s1.username = :username
Run Code Online (Sandbox Code Playgroud)

排名变化变为:

  $drop = max($current_rank - $rank_on_update, 0);
  $increase = max($old_rank_on_update - $rank_on_update, 0);
  $change = $drop ? -$drop : $increase;
Run Code Online (Sandbox Code Playgroud)

更新

  • 评论 1 + 3 - 哎呀,可能搞砸了..上面已经改变了。
  • 评论 2 - 不正确,如果您动态保持分数(所有最新的高分)最新(每次记录新的高分)并假设在计算当前时每个用户有一行排名应该只是高于用户分数 (+1) 的分数计数。一旦数据更新,应该有望避免疯狂的查询!

如果您坚持按时间分隔,并且尚未更新行,则这将适用于新行:

SELECT COUNT(*) + 1 rank
  FROM scores 
 WHERE score >= :score
Run Code Online (Sandbox Code Playgroud)

另一个查询将变为:

SELECT COUNT(*) + 1 rank
  FROM scores s1
  JOIN scores s2
    ON s2.score > s1.score 
    OR (s2.score = s1.score AND s2.time < s1.time) 
 WHERE s1.username = :username
Run Code Online (Sandbox Code Playgroud)

但我至少会尝试 union 来提高性能:

SELECT SUM(count) + 1 rank
  FROM ( 
    SELECT COUNT(*) count
      FROM scores s1
      JOIN scores s2
        ON s2.score > s1.score
     WHERE s1.username = :username
     UNION ALL
    SELECT COUNT(*) count
      FROM scores s1
      JOIN scores s2
        ON s2.score = s1.score
       AND s2.time < s1.time
     WHERE s1.username = :username
       ) counts
Run Code Online (Sandbox Code Playgroud)

索引在(score, time)这里会有所帮助。

就我个人而言,我会为自己省去麻烦,并在相同的排名中保持相同的分数(我认为这是相当标准的)。如果您希望人们能够声称拥有第一个吹牛的权利,只需确保您在任何分数图表上按时间 ASC 排序并包括显示屏中的时间。