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)
如果您将当前的最高分数分离到一个新表中,而所有原始数据都在最近的分数中可用..您已经有效地生成了一个汇总表。
为什么不继续总结和总结所有需要的数据呢?
那么这只是一个你知道什么以及什么时候可以知道的情况:
我会更改您的分数表以包含两个新列:
并在更新/插入每一行时调整这些列。看起来您已经有了可用于在第一次迭代中反向拟合此数据的查询。
现在您的查询变得更加简单
从分数中获取排名:
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)
更新
如果您坚持按时间分隔,并且尚未更新行,则这将适用于新行:
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 排序并包括显示屏中的时间。
| 归档时间: |
|
| 查看次数: |
636 次 |
| 最近记录: |