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

时间:2016-02-08 21:53:46

标签: php mysql sql mysqli combinations

简介

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

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

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

最后,如代码所示:"highcharts": "v4.0.4",


问题

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

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

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

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

这是当前的工作代码:

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

4 个答案:

答案 0 :(得分:3)

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

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

这只是一个你知道什么以及何时可以知道它的案例:

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

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

  • 得分 - id,得分,用户名,昵称,时间,rank_on_update,old_rank_on_update

在更新/插入每一行时调整这些列。 看起来您已经有了可用于为第一次迭代重新配置此数据的查询。

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

从得分中获得排名:

MvcHtmlString

来自用户名:

SELECT COUNT(*) + 1 rank
  FROM scores 
 WHERE score > :score

等级变化变为:

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

<强>更新

  • 评论1 + 3 - 哎呀,可能搞砸了......已经改变了。
  • 评论2 - 不正确,如果您保持最新的得分(所有最新的高分)(每次记录新的高分)并且假设每个用户有一行,那么计算当前等级应该只是一个高于用户得分(+1)的分数。一旦数据是最新的,希望能够避免那种疯狂的查询!

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

  $drop = max($current_rank - $rank_on_update, 0);
  $increase = max($old_rank_on_update - $rank_on_update, 0);
  $change = $drop ? -$drop : $increase;

另一个查询将成为:

SELECT COUNT(*) + 1 rank
  FROM scores 
 WHERE score >= :score

但我至少会尝试结合性能:

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

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 上的索引会对此有所帮助。

就个人而言,我会让自己头疼并保持同等级别(我相信相当标准)。如果你希望人们能够声称首先吹牛的权利,请确保按时间顺序排列ASC任何分数图表并在显示中包含时间。

答案 1 :(得分:0)

我花了很多时间试图弄清楚等级逻辑是什么,并对其进行评论。与此同时,这是一个可以对您的数据运行的连接查询 - 我认为您的解决方案会产生这样的效果:

SELECT s.username, count(*) rank
FROM scores s LEFT JOIN recent r ON s.username != r.username 
WHERE r.istopscore 
AND r.score >= s.score 
AND r.time <= s.time 
AND (r.score-s.score + s.time-r.time) 
GROUP BY s.username
ORDER BY rank ASC;

+----------+------+
| username | rank |
+----------+------+
| Beta     |    1 |
| Alpha    |    2 |
| Echo     |    3 |
+----------+------+

(请注意,最后一个AND只是为了确保您不会考虑r.score == s.score&amp;&amp; r.time == s.time - 我猜这将是&#34领带&#34;游戏?)

答案 2 :(得分:0)

我不是MySQL人,但我认为在任何RDBMS中使用自联接进行排名都是不好的做法。您应该考虑使用排名功能。但是MySQL中没有排名功能。但有workarounds

答案 3 :(得分:0)

为了向前推进,必须在此进行一些假设。我假设分数表每个用户名只有一个条目&#39;这在某种程度上等同于昵称。

试试这个,

如果我有一个正常工作的数据库,这将很快找出并测试,但基本上你正在进行“查询”。您正在所选字段中运行,并且您正在构建包含所有记录的临时表并将其过滤掉。

       select a.nickname
            , count(distinct b.username) as rank
            , t.time
            , t.username
            , t.score
        from
        (  
                select 
                    a.nickname
                    , b.username
                from (select * from scores where nickname=? ) a
                    left join (select * from recent where istopscore = 1) as b
                on (
                        b.score > a.score and b.time <= a.time -- include the b record if the b score is higher
                        or 
                        b.score = a.score and b.time < a.time and a.username != b.username -- include b if the score is the same,  b got the score before a got the score
               )
         ) tmp
         join  scores t  on (t.nickname = tmp.nickname)
         where t.nickname = ?

我没有尝试解决你以后的逻辑,你可以使用相同的理论,但除非你能确认这个方法返回正确的行,否则不值得尝试。

如果您想深入了解,您应该创建一些数据集并完全设置SQL Fiddle。