3

かなり大きなデータセットと 2 つの結合を必要とするクエリがあるため、クエリの効率は非常に重要です。結合の結果に基づいて、条件を満たすデータベースから 3 つのランダムな行を取得する必要があります。ここでは、最も明白な解決策が非効率的であると指摘されています。

[これらのソリューション] すべてのテーブルのシーケンシャル スキャンが必要です (各行に関連付けられたランダム値を計算する必要があるため、最小のものを決定できるようにするため)。

ただし、作成者が提案した方法 ( SELECT * FROM table WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table) LIMIT 1num_value は ID) は、一部の ID が欠落している可能性があるため (一部の行がユーザーによって削除された可能性があるため)、私には機能しません。

では、私の状況で 3 つのランダムな行を取得する最も効率的な方法は何でしょうか?

編集:ソリューションは純粋な SQL である必要はありません。私もPHPを使っています。

4

2 に答える 2

3

多くの結果は必要ないため、 と を使用しLIMITた興味深いオプションがいくつかありますOFFSET

id一意で並べ替えに適した列を想定します。

最初のステップは、 a を実行してから、PHP でCOUNT(id)から までの 3 つの乱数を選択することです。(それを行う方法は別の問題であり、最善のアプローチは合計行数と必要な数によって異なります)。0COUNT(id) - 1

2 番目のステップには 2 つのオプションがあります。選択した乱数が 0、15、2234 であるとします。どちらも PHP にループがあります。

// $offsets = array(0, 15, 2234);
foreach ($offsets as $offset) {
    $rows[] = execute_sql('SELECT ... ORDER BY id LIMIT 1 OFFSET ?', $offset);
}

またはビルドしUNIONます。注: ORDER BY を使用しているため、サブセレクトが必要です。

// $offsets = array(0, 15, 2234);
$query = '';
foreach ($offsets as $index => $offset) {
    if ($query) $query .= ' UNION ';
    $query .= 'SELECT * FROM (SELECT ... ORDER BY id LIMIT 1 OFFSET ?) Sub'.$index;
}
$rows = execute_sql($query, $offsets);
于 2012-09-04T11:06:01.853 に答える
2

RAND() 呼び出しを ORDER BY 句に追加すると、ID を無視できるようになります。これを試して:

SELECT * FROM table WHERE ... ORDER BY RAND() LIMIT 3;

パフォーマンスの問題が指摘された後、最善の策は次のようなものかもしれません (PHP を使用):

$result = PDO:query('SELECT MAX(id) FROM table');
$max    = $result->fetchColumn();
$ids    = array();
$rows   = 5;

for ($i = 0; $i < $rows; $i++) {
    $ids[] = rand(1, $max);
}

$ids     = implode(', ', $ids);
$query   = PDO::prepare('SELECT * FROM table WHERE id IN (:ids)');
$results = $query->execute(array('ids' => $ids));

この時点で、最初の 3 つの結果を選択できるはずです。このアプローチの唯一の問題は、削除された行を処理することです。少なくとも 3 つの結果が返されなかった場合は、$rows 変数を変更するか、別のクエリを実行するロジックを追加する必要があります。

于 2012-04-07T13:53:02.597 に答える