高分数据库
这是一个高级/设计类型的问题,而不是任何具体的问题。
在这个游戏中 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')");
}
?>
This is a high level/design type question rather than anything specific.
In this game http://koreanwordgame.com/ users have to get as many correct answers as possible in a minute (though its set to 5 seconds for development purposes). As soon as the minute is up I fire an Ajax call with JQuery passing the users score.
On the server side I use this SQL query to get the 5 scores above and 5 below
$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)";
This returns an HTML table to the client which you will see upon completing the (5 sec) challenge (you don't have to get any answers right).
Now, ultimately I want the table to be about 100 rows long (although it will still only display the surrounding 10 cells, 5 above, 5 below), with the rows position (ID) determined not chronological as it is now but by the score, so that if someone gets 1000 answers right they would go straight into position 1 on the database. Obviously if a user gets lower than any of the 100 rows already existing in the table then it should simply be discarded. Then when the table comes back I want a text input to appear inside the table cell where the user is placed so they can enter their name and fire off another Ajax call so their entry gets inserted into the right place in the database.
I know it would be easier just to ask the user to enter their details first and then scoop off the top 10 results whether they got there or not, but the idea is that I want anyone to be able to get some kind of entry as this will encourage people to play more.
A tough one perhaps..I certainly haven't been able to find anything similar by Googling, all help much appreciated!
EDIT:
In case anyone is interested this is what I ended up with, it's far from finished I need to make it way more robust for example entering an anonymous username in case the user closes the browser and also prevent double posts (though this will be client side).
but thank you everyone for the help, I would not have done it without you. If you see any obvious improvements that could be made feel free to point them out!
<?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')");
}
?>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你没有说你的问题出在哪里,但我认为这主要是对 SQL 的误解。这是我将如何完成整个应用程序。
对于 SQL 模式:
对于工作流程:
json_encode()
返回一个数组/对象)。否则,询问用户的名称,并发送新的 AJAX 请求。 SQL 可以保持简单:
INSERT INTO Highscore SET name=?, Score=?, when=NOW()
每次插入后,删除最低分数
DELETE FROM Highscore ORDER BY 分数 ASC,当 DESC LIMIT 1 时
如果你想防止作弊,你可以需要在 ajax 调用中添加哈希值。例如,发送(username, Score, hash=md5(username.score."secretphrase")并检查哈希是否仍然与用户名和分数匹配。
UPDATE Highscore SETrank =rank + 1 WHERE Score 。
You did not say where lies your problem, but I suppose it's mostly a misconception about SQL. Here's how I would do the whole app.
For the SQL schema:
For the workflow:
json_encode()
in PHP to return an array/object).Else, ask the user its name, and send a new AJAX request. The SQL can be kept simple:
INSERT INTO Highscore SET name=?, score=?, when=NOW()
After each insertion, delete the lowest score
DELETE FROM Highscore ORDER BY score ASC, when DESC LIMIT 1
If you want to prevent cheating, you'll need to add hashes in your ajax calls. For instance, send (username, score, hash=md5(username.score."secretphrase") and check the hash still matches the username and score.
UPDATE Highscore SET rank = rank + 1 WHERE score < ?
.我正在吃泡菜来了解你到底在追求什么。下面是一个将结果插入到高分中的查询。
I'm havinga pickle to understand what you're exactly after. Here's a query to insert the result in the highscore.
我无法完全理解你的问题,但基于此:
我怀疑您希望向用户显示的分数表看起来像这样:
我赞同 Mytskine 的最初建议,即您根本不应该尝试将排名存储在数据库中 - 保留排名列最新的是可能的,比如Mytskine 在他们的答案末尾显示,但这是大量的工作,但收效甚微。只需让您的分数表由诸如
name
和score
和time
等明显的列组成,再加上一个 AUTO_INCREMENT 列id
。现在,假设您要显示得分最高的 N 位玩家,以及新得分前后的 K 位玩家。首先,我们需要计算出玩家的排名:
这里,
?
是玩家得分的占位符。这会计算有多少玩家的得分高于我们将在下面插入的得分。如果在平局的情况下我们倾向于最新的分数,则该分数加一将是玩家的排名。让我们用 R 来表示这个等级。现在,如果 R < N + K,我们可能只显示前 N + 2*K (或 max(N, R+K) 或其他)条目:
否则,我们需要分三个步骤进行:首先,获取前 N 个条目:
然后是播放器上方的 K 个条目:
最后是播放器下方的 K 个条目:
请注意,第二个结果集将按相反顺序排列,但这没关系 - 只需在 PHP 中反转即可。现在我们需要为我们获取的条目分配排名,但这很简单:第一组的排名为 1 到 N,第二组(反转后)的排名为 RK 到 R-1,最后一组的排名为 R+ 1 至 R+K。 (当然,R 等级属于当前玩家。)
最后,在玩家输入姓名后,我们可以简单地将玩家的分数插入到数据库中:
如果需要,您还可以从数据库中删除最低分数在这一点上限制其规模,但实际上,除非你有数十亿玩家,否则这可能并不重要。 (单个分数条目将占用几十个字节,主要取决于玩家输入的名称的长度,因此一百万个条目仍然只有几十兆字节。)但是,您应该记住在
(score, time)
以保持查询高效。I couldn't entirely make sense of your question, but based on this:
I suspect you want the score table shown to the user to look something like this:
I second Mytskine's initial suggestion that you shouldn't try to store the ranks in the DB at all — keeping a rank column up to date is possible, like Mytskine shows at the end of their answer, but it's a lot of work for little if any gain. Just have your score table consist of the obvious columns like
name
andscore
andtime
, plus maybe an AUTO_INCREMENT columnid
.Now, let's say you want to show the N highest-scoring players, plus K players before and after the new score. First, we need to figure out the player's rank:
Here, the
?
is a placeholder for the player's score. This counts how many players have a higher score than the one we're going to insert below. If we favor the latest scores in the case of ties, this plus one will be the player's rank. Let's denote this rank by R.Now, if R < N + K, we might was well just show the top N + 2*K (or max(N, R+K) or whatever) entries:
Otherwise, we need to do it in three steps: first, fetch the top N entries:
then, the K entries above the player:
and finally the K entries below the player:
Note that the second result set will be in reverse order, but that's OK — just reverse it in PHP. Now we need to assign ranks to the entries we've fetched, but that's simple: the first set has ranks 1 to N, the second set (after reversal) has ranks R-K to R-1, and the last set has ranks R+1 to R+K. (Rank R belongs to the current player, of course.)
Finally, after the player has entered their name, we can insert the player's score into to the database simply with:
If you want, you can also remove the lowest scores from the database at this point to limit its size, but in practice it probably won't matter unless you have literally billions of players. (A single score entry is going to take a few dozen bytes, mostly depending on how long a name the player enters, so a million entries will still be just a few dozen megabytes.) You should, however, remember to create an index on
(score, time)
to keep the queries efficient.