【问题标题】:high score database高分数据库
【发布时间】:2011-10-05 00:30:08
【问题描述】:

这是一个高级/设计类型的问题,而不是任何具体的问题。

在这个游戏http://koreanwordgame.com/ 中,用户必须在一分钟内获得尽可能多的正确答案(尽管出于开发目的将其设置为 5 秒)。时间一到,我就会使用 JQuery 进行 Ajax 调用,从而使用户得分。

在服务器端,我使用这个 SQL 查询来获取上面的 5 个分数和下面的 5 个分数

$getquery = "(SELECT * 
FROM highscore 
WHERE score >= '$score'
ORDER BY score ASC
LIMIT 5)

UNION ALL

(SELECT * 
FROM highscore 
WHERE score < '$score'
ORDER BY score DESC
LIMIT 5)";

这会向客户端返回一个 HTML 表格,您将在完成(5 秒)挑战后看到该表格(您不必正确获得任何答案)。

现在,最终我希望表格的长度约为 100 行(尽管它仍将仅显示周围的 10 个单元格,上方 5 个,下方 5 个),确定的行位置 (ID) 不像现在那样按时间顺序排列,而是根据分数,如果有人答对了 1000 个答案,他们会直接进入数据库中的第 1 位。显然,如果用户低于表中已经存在的 100 行中的任何一个,那么它应该被简单地丢弃。然后,当表格返回时,我希望文本输入出现在用户所在的表格单元格中,以便他们可以输入他们的姓名并触发另一个 Ajax 调用,以便将他们的条目插入数据库中的正确位置。

我知道让用户先输入他们的详细信息,然后从前 10 个结果中挖掘出他们是否到达那里会更容易,但我的想法是我希望任何人都能够获得某种进入,因为这将鼓励人们玩得更多。

也许是一个艰难的..我当然无法通过谷歌搜索找到任何类似的东西,非常感谢所有帮助!

编辑:

如果有人感兴趣,这就是我最终得到的结果,它还远未完成,我需要让它变得更健壮,例如输入匿名用户名以防用户关闭浏览器并防止重复发帖(尽管这会成为客户端)。

但谢谢大家的帮助,没有你们我不会做到。如果您发现任何可以改进的明显改进,请随时指出!

<?php

$dbcon = mysql_connect("localhost", "XXXX", "XXXX") or      die(mysql_error());
mysql_select_db("tulesblo_koreangame", $dbcon) or die(mysql_error());
mysql_query("SET NAMES utf8");

$name = $_POST["name"];
$email = $_POST["email"];
$score = $_POST["score"];
$table = "";
$submit = "";
$input = "";
$newposition = $_POST['position'];

$position = mysql_query("(SELECT position 
    FROM highscore 
   WHERE score < '$score'
ORDER BY score DESC
   LIMIT 1)");

if(!$name){

$gethigherrows = "(SELECT * 
    FROM highscore 
   WHERE score >= '$score'
ORDER BY score ASC
   LIMIT 5)";

$getlowerrows = "(SELECT * 
    FROM highscore 
   WHERE score < '$score'
ORDER BY score DESC
   LIMIT 5)";

$higherrows= mysql_query($gethigherrows);
$lowerrows= mysql_query($getlowerrows);

if(mysql_error())echo mysql_error();

while($row=mysql_fetch_array($higherrows)) 
{
    $uppertable .= "<tr><td>$row[position]</td><td>$row[name]</td>    <td>$row[score]</td></tr>";
}

$x = 0; 
if (mysql_num_rows($lowerrows) > 0)
{   mysql_query("UPDATE highscore SET position = position + 1 WHERE score <  '$score'")or die("update failed");
    while($row=mysql_fetch_array($lowerrows)) 
    {   
        if ($x == 0)
            {$position = $row['position'];};
        $x++;
        $newpos = $row[position]+1;
        $lowertable.= "<tr><td>$newpos</td><td>$row[name]</td>    <td>$row[score]</td></tr>";
    }
    $input = "<tr><td id='position'>$position</td><td><input  id='nameinput'type='text' /></td><td>$score</td></tr>";
    $submit = "<br />Enter email if you want to receive a prize!<br /><input  id='emailinput'type='text' /><br /><input id='submithighscore'type='submit'  value='Submit'>";
}

$table .= "<table id='scoretable'><tr><th>Position</th><th>Name</th><th>Score</th></tr>";
$table .= $uppertable;
$table .= $input;
$table .= $lowertable;
$table .= "</table>";
$table .= $submit;
$table .= "<br /><span class='msg'></span>";
echo $table;

}else{  echo($newposition);
mysql_query("INSERT INTO highscore VALUES (NULL, '$score', '$name', '$email',     '$newposition')");
}

?>

【问题讨论】:

  • 您能问一下您的“高级/设计类型问题”吗?从你的描述中我看不出有什么困难。您需要什么帮助?
  • 抱歉,我知道这听起来很菜鸟,但我更像是一个前端人员,我对 SQL 的经验很少但是我意识到我应该分别执行 2 个 SQL 查询,然后当我用 PHP 构建 HTML 表时,将输入框放在两组结果之间,这应该很明显。我想剩下的唯一问题是如何为每一行分配一个位置编号?这将需要对表中的所有记录进行排序,并在它们被选中之前为其分配一个等级/位置。插入新记录时有什么方法可以将其构建到表中?
  • 您有一个 SQL 注入漏洞,请参阅:stackoverflow.com/questions/332365/…

标签: php mysql sql database-design jquery


【解决方案1】:

您没有说问题出在哪里,但我想这主要是对 SQL 的误解。以下是我将如何完成整个应用程序。

对于 SQL 架构:

  • 您不应该关心数据库中行的顺序。您可以在查询(SELECT 等)中设置顺序。没有“正确的位置”可以插入。
  • 出于同样的原因,您不需要“排名”列。
  • 您的表可以很简单:id (INT AUTO_INCREMENT)、name、score、when(timestamp)。
  • 用 100 个虚拟分数对其进行初始化。

对于工作流程:

  • 现在不关心性能,以可读性为目标。
  • 正如您所写,一旦玩家完成,使用 AJAX 查找周围的 10 个分数。您已经拥有这部分的 SQL 查询。您可以使用 PHP 在服务器端准备 HTML,也可以发送原始数据以使用 Javascript 构建 HTML。这可以在一个请求中完成(在 PHP 中使用 json_encode() 返回一个数组/对象)。
  • 如果没有以下分数,那么它不是 100 个最佳分数之一,什么也不做。
  • 否则,向用户询问其名称,然后发送新的 AJAX 请求。 SQL 可以保持简单:

    INSERT INTO Highscore SET name=?, score=?, when=NOW()

  • 每次插入后,删除最低分

    DELETE FROM Highscore ORDER BY score ASC, when DESC LIMIT 1

  • 如果您想防止作弊,您需要在您的 ajax 调用中添加哈希值。例如,发送 (username, score, hash=md5(username.score."secretphrase") 并检查哈希是否仍然与用户名和分数匹配。

  • 如果您想与分数一起显示排名,请在表格中放置一个“排名”列。当您插入一个分数时,将其排名设置为较​​低分数的最大排名(您已经为第一个 AJAX 请求计算了这个,所以在第二个 AJAX 请求中发送它)。然后UPDATE Highscore SET rank = rank + 1 WHERE score &lt; ?

【讨论】:

  • 非常感谢,我认为这让我对如何解决这个问题有了更清晰的认识,我对 SQL 缺乏经验,所以我不确定我应该如何考虑它
  • 特别是最后一行 SQL 是我真正想要的,非常感谢您的帮助,今晚我将尝试实现它,然后投票给您的答案
  • 如果你有兴趣看看我最终得到了什么,请看上面:)
  • 进一步改进的2条建议:1) PHP中不推荐使用“mysql”扩展,因此建议将mysql_*函数替换为mysqli,或者使用PDO反而。 2) 如果用户名包含引号会怎样?如果您的 PHP 服务器上没有启用魔术引号,您将面临 sql 注入的风险。尝试了解魔术引号、mysql 转义和准备查询。玩得开心。
  • mysql_real_escape_string() 不能防止 SQL 注入吗?我会研究 mysqli 谢谢
【解决方案2】:

我很想知道你到底想要什么。这是一个将结果插入高分的查询。

$str_user_id = $_GET['user_id'];
$str_score = $_GET['score'];

mysql_query("
INSERT INTO highscore (position, user_id, score)
SELECT (@rownum := $rownum + 1) AS position, user_id, score
FROM (SELECT user_id, score
      FROM (SELECT '{$str_user_id}' AS user_id, '{$str_score}' AS score
            UNION ALL
            SELECT user_id, score
            FROM highscore) AS h, (@rownum := 0) AS vars
      WHERE '{$str_score}' <= 100
      ORDER BY score ASC) AS h2)
LIMIT 100
ON DUPLICATE KEY UPDATE user_id = VALUES(user_id), score = VALUES(score)
");

【讨论】:

  • 哇,看起来好复杂,我测试一下再回复你
【解决方案3】:

我无法完全理解您的问题,但基于此:

“我知道让用户先输入他们的详细信息,然后从前 10 个结果中剔除他们是否到达那里会更容易,但我的想法是我希望任何人都能够获得某种结果进入,因为这将鼓励人们玩更多”

我怀疑您希望向用户显示的分数表看起来像这样:

    1. John Doe (score: 1,000,000)
    2. Larry Roe (score: 999,999)
    3. Jane Doe (score: 985,742)
 ...
 5746. Susan Player (score: 894)
 5747. *** YOU *** (score: 893)
 5748. Joe R. Player (score: 889)

我赞同 Mytskine 的最初建议,即您根本不应该尝试将排名存储在数据库中 - 保持排名列是最新的是可能的,就像 Mytskine 在他们的答案末尾显示的那样,但这是很多工作几乎没有任何收获。只需让您的分数表包含 namescoretime 等明显的列,还可能加上一个 AUTO_INCREMENT 列 id

现在,假设您要显示得分最高的 N 个玩家,以及新得分前后的 K 个玩家。首先,我们需要弄清楚玩家的排名:

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

这里,? 是玩家得分的占位符。这会计算有多少玩家的分数高于我们将在下面插入的分数。如果我们在平局的情况下支持最新的分数,这个加一将是玩家的排名。让我们用 R 来表示这个等级。

现在,如果 R

SELECT * FROM scores ORDER BY score DESC, time DESC LIMIT N+2*K

否则,我们需要分三步完成:首先,获取前 N 个条目:

SELECT * FROM scores ORDER BY score DESC, time DESC LIMIT N

那么,播放器上方的 K 个条目:

SELECT * FROM scores WHERE score > ? ORDER BY score ASC, time ASC LIMIT K

最后是播放器下方的 K 个条目:

SELECT * FROM scores WHERE score <= ? ORDER BY score DESC, time DESC LIMIT K

请注意,第二个结果集将以相反的顺序排列,但这没关系 - 只需在 PHP 中将其反转即可。现在我们需要为我们获取的条目分配等级,但这很简单:第一组的等级为 1 到 N,第二组(反转后)的等级为 R-K 到 R-1,最后一组的等级为 R+ 1 到 R+K。 (当然,等级 R 属于当前玩家。)

最后,当玩家输入他们的名字后,我们可以简单地将玩家的分数插入到数据库中:

INSERT INTO scores (name, score, time) VALUES (?, ?, ?)

如果您愿意,您也可以此时从数据库中删除最低分数以限制其大小,但实际上,除非您有数十亿玩家,否则这可能无关紧要。 (单个分数条目将占用几十个字节,主要取决于玩家输入的名称多长时间,因此一百万个条目仍然只有几十兆字节。)但是,您应该记住在(score, time) 保持查询效率。

【讨论】:

  • 是的,这基本上就是我希望它看起来的样子,只是我想在结果的 html 表中显示一个文本输入,以便玩家在输入他们的名字之前看到他们在哪里。所以我真的需要做 2 个 Ajax 调用,一个先提取结果,然后 1 个插入新结果。
  • @user555116:是的,这也有效。事实上,我只是编辑了我的答案,把插入放在最后。
  • 如果你有兴趣看看我最终得到了什么,请看上面:)
猜你喜欢
  • 1970-01-01
  • 2011-07-23
  • 2013-09-25
  • 2016-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多