0

我的问题几乎是不言自明的,但我不能完全解决它以使其尽可能高效。
我想从 MySQL 数据库中选择一个随机条目。我希望它尽可能快且尽可能高效(这始终是目标,不是吗?)。当我选择该行时,我想选择另一行,但与之前的行不同。如果我选择 10 行,我希望第 11 行与所有其他行不同(可以说是唯一的 :))。但是当我用完行时,我想“报告错误”。

才能触及问题的核心。我正在使用 PHP 和 MySQL。我有一个包含已选择标题的输入数组。然后我得到数据库中所有项目的计数,所以我知道我可以“循环通过最大值”多少次。让我们粘贴代码来看看我们在这里处理什么。

try
{
    $db = new PDO("mysql:host=localhost;dbname=xxxxx;charset=utf8", "xxxx", "xxxx");

    $played = explode(":;:", $_POST['items']); //All already selected items are in $_POST separated by :;:

    $sql = "SELECT count(id) AS count FROM table"; //Lets get the total count of items

    $query = $db->prepare($sql);
    $query->execute();
    $result = $query->fetch(PDO::FETCH_ASSOC);

    $count = $result['count']; //There we are...
    $i = 0; //Index counter so we dont exceed records.. well kinda (more on that below)

    do //do while because we want something to be selected first
    {
        $sql = "SELECT FLOOR(RAND() * COUNT(*)) AS offset FROM table"; //From here

        $query = $db->prepare($sql);
        $query->execute();
        $result = $query->fetch(PDO::FETCH_ASSOC);
        $offset = $result['offset'];

        $sql = "SELECT itemXML FROM table LIMIT $offset, 1";

        $query = $db->prepare($sql);
        $query->execute();
        $result = $query->fetch(PDO::FETCH_ASSOC); //To here is some code to randomly select a record "as efficiently as possible"..

        $output = Array();

        $xml = simplexml_load_string($result['itemXML']);

        $i++;
    } while (in_array($xml->{"title"}, $played) && $i < $count); //While record title is in array and while we have not exceeded the total number of records (that's the logic but it isint that simple is it?)

    if ($i >= $count)
    {
        die("400"); //Just a random status code which I parse with the client.
    }

    $itemArr = Array("whatever" => $xml->{"whatever-attr"}, "title" => $xml->{"title"});
    array_push($output, $itemArr); Lets push this to out array

    echo json_encode($output); //Aaaaand finally lets print out the results
}
catch (Exception $e) //If anything went wrong lets notify our client so he can respond properly
{
    $output = Array("error" => $e->getMessage());
    die(json_encode($output));
}

是的,很好.. 问题是如果有 10 条记录,选择了 9 行并且索引计数器$i变得更大或等于 10 并且随机记录都在数组中,该怎么办?然后我们有一行应该被选中,但它没有。

我该如何解决这个问题?您的帮助将不胜感激!
如果我解释得不够好,请告诉我,我会更加努力。

4

2 回答 2

2

我认为您在这里采取了错误的方法。您不应该需要遍历数据库一次查询一条记录。

如果您需要选择 10 条记录,只需像这样选择 RAND() 排序的 10 条记录

SELECT * FROM table
ORDER BY RAND()
LIMIT 10;

或者,如果您想从选择中省略某些 ID

SELECT * FROM table
WHERE id NOT IN (?, ?, ?, ...)
ORDER BY RAND()
LIMIT 10;

或者,如果您要省略的 id 存储在另一个表中

SELECT * FROM table
LEFT OUTER JOIN omit_table ON table.id = omit_table.id
WHERE omit_table.id IS NULL
ORDER BY RAND()
LIMIT 10;
于 2013-03-07T17:26:42.703 回答
1

假设您已经在下表中填充了数据:

TABLE mydata
  id INT AUTOINCREMENT PRIMARYKEY
  name VARCAHAR
  ...

我们为一些非随机映射创建下表:

TABLE shufflemap
  id INT AUTOINCREMENT PRIMARYKEY
  data_id INT UNIQUEINDEX

我们执行以下操作:

$rs = $dbh->query('SELECT id FROM mydata');
shuffle($rs);
foreach($rs as $data_id) {
    $dbh->query('INSERT INTO shufflemap (data_id) VALUES (?)', array($data_id));
}

现在如果我们想添加行怎么办?您可以TRUNCATE表并重新运行上述代码,或者:

$my_new_id = 1234; //the ID of the new row inserted into `mydata`
$rs = $dbh->query('SELECT COUNT(*) AS 'count' from shufflemap');
$target = rand(0,$rs[0]['count']);
$rs = $dbh->query('SELECT id, data_id FROM shufflemap LIMIT ?,1', array($target));
$swap_id = $rs[0]['id'];
$swap_data_id = $rs[0]['data_id'];
$dbh->query('UPDATE shufflemap SET data_id = ? WHERE id = ?', array($my_new_id, $swap_id));
$dbh->query('INSERT INTO shufflemap (data_id) VALUES (?)', array($swap_data_id));

它以相当有效的方式从 shufflemap 表中选择随机条目,将 data_id 替换为新的,并将旧的添加到表的末尾。

使用这种方式,您可以拥有看似随机的数据而没有重复,并且仍然可以通过在 JOIN、子查询或您能想到的任何其他方式中使用 shufflemap 表来使用表中的所有正确索引。

编辑

假设 mydata 表有一个字段指示每个字段与哪个客户端或用户相关联,即:

TABLE mydata
  id INT AUTOINCREMENT PRIMARYKEY
  client_id INT
  name VARCAHAR
  ...

可以通过以下方式检索仅该客户数据的打乱列表:

SELECT d.*
FROM mydata d INNER JOIN shufflemap s
  ON d.id = s.data_id
WHERE client_id = ?
ORDER BY s.id

排除已播放项目的列表?

SELECT d.*
FROM mydata d INNER JOIN shufflemap s
  ON d.id = s.data_id
WHERE client_id = ?
  AND d.id NOT IN(?,?,?,...)
ORDER BY s.id
于 2013-03-07T17:51:04.360 回答