高分数据库

发布于 2024-12-08 09:39:00 字数 3456 浏览 1 评论 0原文

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

在这个游戏中 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

今天小雨转甜 2024-12-15 09:39:00

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

对于 SQL 模式:

  • 您不应该关心数据库中行的顺序。您可以在查询中设置顺序(SELECT 等)。没有“正确的位置”可以插入。
  • 出于同样的原因,您不需要“排名”列。
  • 你的表可以很简单:id (INT AUTO_INCRMENT)、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 分数 ASC,当 DESC LIMIT 1 时

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

  • 如果您想与分数一起显示排名,请在表格中放置一个“排名”列,当您插入分数时,将其排名设置为较​​低分数的最高排名(您已经为第一个计算了该排名) AJAX 请求,因此在第二个 AJAX 请求中发送),然后 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:

  • You shouldn't care about the order of the rows in the DB. You can set the order in the queries (SELECT, etc). There is no "right place" to insert.
  • You don't need a "rank" column, for the same reason.
  • Your table can be as simple as: id (INT AUTO_INCREMENT), name, score, when (timestamp).
  • Initialize it with 100 dummy scores.

For the workflow:

  • Do not care for performance now, aim for readability.
  • As you wrote, once a player has finished, use AJAX to find the 10 surrounding scores. You already have the SQL queries for this part. You can either prepare the HTML on the server side in PHP, or send raw data in order build the HTML with Javascript. This can be done in one request (use json_encode() in PHP to return an array/object).
  • If there are no scores below, then it's not one of the 100 best scores, do nothing.
  • 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.

  • If you want to display the ranks along with the scores, then put a "rank" colum in your table. When you insert a score, set its rank to the max rank of the lower scores (you already computed this for the first AJAX request, so send it in the second AJAX request). Then UPDATE Highscore SET rank = rank + 1 WHERE score < ?.
云柯 2024-12-15 09:39:00

我正在吃泡菜来了解你到底在追求什么。下面是一个将结果插入到高分中的查询。

$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)
");

I'm havinga pickle to understand what you're exactly after. Here's a query to insert the result in the highscore.

$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)
");
街角卖回忆 2024-12-15 09:39:00

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

“我知道先要求用户输入他们的详细信息,然后无论他们是否到达那里,然后捞出前 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 < N + K,我们可能只显示前 N + 2*K (或 max(N, R+K) 或其他)条目:

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,第二组(反转后)的排名为 RK 到 R-1,最后一组的排名为 R+ 1 至 R+K。 (当然,R 等级属于当前玩家。)

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

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

如果需要,您还可以从数据库中删除最低分数在这一点上限制其规模,但实际上,除非你有数十亿玩家,否则这可能并不重要。 (单个分数条目将占用几十个字节,主要取决于玩家输入的名称的长度,因此一百万个条目仍然只有几十兆字节。)但是,您应该记住在(score, time) 以保持查询高效。

I couldn't entirely make sense of your question, but based on this:

"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"

I suspect you want the score table shown to the user to look something like this:

    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)

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 and score and time, plus maybe an AUTO_INCREMENT column id.

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:

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

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:

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

Otherwise, we need to do it in three steps: first, fetch the top N entries:

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

then, the K entries above the player:

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

and finally the K entries below the player:

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

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:

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文