从PHP数据库中选择一个随机行,但不是一个相等的行

时间:2013-03-07 17:22:00

标签: php mysql database arrays

我的问题几乎是自我解释,但我不能努力使其尽可能高效 我想从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,随机记录全部在数组中。那么我们应该选择一行,但不是。

我该如何解决这个问题?非常感谢您的帮助! 如果我没有充分解释它让我知道我会更加努力。

2 个答案:

答案 0 :(得分: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;

答案 1 :(得分: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表来使用表中的所有正确索引。 / p>

修改

假设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