2012-04-07 3 views
3

Ich habe eine ziemlich große Datenmenge und eine Abfrage, die zwei Joins erfordert, so dass die Effizienz der Abfrage sehr wichtig für mich ist. Ich muss 3 zufällige Zeilen aus der Datenbank abrufen, die eine Bedingung basierend auf dem Ergebnis eines Joins erfüllen. Most obvious solution wird als ineffizient here darauf hingewiesen, weilMySQL: Was ist die effizienteste Möglichkeit, mehrere zufällige Zeilen zu wählen

[Diese Lösungen] eine sequentielle Abtastung aller Tabellen müssen (weil der Zufallswert mit jeder Zeile assoziiert muss berechnet werden, - so dass die kleinste bestimmt werden kann) , die sogar für mittelgroße Tische ziemlich langsam sein können.

Jedoch ist das Verfahren dort vom Autor vorgeschlagen (SELECT * FROM table WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table) LIMIT 1 wo num_value ist ID) nicht für mich arbeiten, weil einige IDs fehlen könnte (da einige Zeilen, die von Benutzern gelöscht wurden).

Also, was wäre die effizienteste Möglichkeit, 3 zufällige Zeilen in meiner Situation zu erhalten?

EDIT: die Lösung muss nicht reine SQL sein. Ich benutze auch PHP.

+0

Muss dies reine SQL sein oder haben Sie Zugriff auf eine Skriptsprache? – liquorvicar

+1

Duplizieren? http://stackoverflow.com/questions/4329396/mysql-select-10-random-rows-from-600k-rows-fast – joelparkerhenderson

+0

Ich benutze auch PHP. – sbichenko

Antwort

2

Wenn Sie Ihren RAND() - Aufruf in die ORDER BY-Klausel einfügen, können Sie die ID ignorieren. Versuchen Sie folgendes:

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

Nach Performance-Probleme hingewiesen, die beste Wahl sein kann, etwas in dieser Richtung (unter Verwendung von 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)); 

An diesem Punkt sollten Sie die ersten 3 auswählen können Ergebnisse. Das einzige Problem bei diesem Ansatz ist der Umgang mit gelöschten Zeilen. Möglicherweise müssen Sie entweder die $ rows var-Datei auffüllen oder Logik hinzufügen, um eine weitere Abfrage durchzuführen, falls Sie nicht mindestens 3 Ergebnisse zurück erhalten.

+0

Dies scheint ziemlich ineffizient zu sein, weil es, wie auf der Seite, die ich verlinkt habe, erwähnt, einen sequentiellen Scan der gesamten Tabelle erzwingt (was in meinem Fall groß ist). Oder habe ich deine Antwort falsch verstanden? – sbichenko

+0

Das scheint der Fall zu sein. Ich denke nicht, dass es eine gute Möglichkeit geben wird, dies rein über MySQL zu tun, während immer noch gelöschte IDs berücksichtigt werden. Am besten ist es wahrscheinlich, die höchste ID auszuwählen und dann> 3 zufällige IDs auszuwählen, damit Sie genug haben, um gelöschte Zeilen zu berücksichtigen. – clexmond

+0

Dieser Code wird möglicherweise auch weniger Zeilen als erwartet zurückgeben, da der Zufalls-ID-Generator nicht überprüft, dass die IDs nicht bereits generiert wurden - 'WHERE ID IN (1,1,2)' gibt 2 Zeilen zurück. – StampyCode

2

Da Sie nicht viele Ergebnisse wünschen, gibt es ein paar interessante Optionen mit LIMIT und OFFSET.

Ich gehe von einer id Spalte aus, die einzigartig und zum Sortieren geeignet ist.

Der erste Schritt besteht darin, eine COUNT(id) auszuführen, und wählen Sie dann zufällige 3 Zahlen von 0 zu COUNT(id) - 1 in PHP. (Wie dies zu tun ist, ist eine separate Frage, und die beste Vorgehensweise hängt von der Anzahl der Zeilen insgesamt und der Anzahl ab, die Sie möchten).

Der zweite Schritt hat zwei Optionen. Angenommen, die Zufallszahlen Sie ausgewählt sind 0, 15, 2234. Entweder haben eine Schleife in PHP

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

oder eine UNION bauen. Hinweis: Dies erfordert Unterauswahlen, weil wir ORDER BY verwenden.

// $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); 
Verwandte Themen