0

私の問題はほとんど自明ですが、可能な限り効率的にするためにそれをうまく解決することはできません。
MySQLデータベースからランダムなエントリを選択したい。できるだけ速く、できるだけ効率的にしたいと思っています(それが常に目標ですよね?)。その行を選択するときに、別の行を選択したいのですが、前の行と同じではありません。10行を選択した場合、11行目を他のすべての行とは異なるものにします(一意としましょう:))。しかし、行が足りなくなったら、「エラーを報告」したいと思います。

問題の核心にたどり着くため。MySQLでPHPを使用しています。すでに選択されているタイトルを含む入力配列があります。次に、データベース内のすべてのアイテムの数を取得するので、「最大ループ」を何回実行できるかがわかります。コードを貼り付けて、ここで何を扱っているかを見てみましょう。

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以上になり、ランダムレコードがすべて配列に含まれることです。次に、選択されるべきであるが選択されていない行が1つあります。

そして、どうすればこれを修正できますか?あなたの助けは大歓迎です!
私がそれを十分に説明しなかったならば、私がもっと一生懸命に努力することを私に知らせてください。

4

2 に答える 2

2

ここでは間違ったアプローチを取っていると思います。一度に1つのレコードをクエリするデータベースをループする必要はありません。

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 に答える