554 Stimmen

MySQL wähle 10 zufällige Zeilen aus 600.000 Zeilen schnell

Wie kann ich am besten eine Abfrage schreiben, die 10 Zeilen zufällig aus insgesamt 600.000 auswählt?

19 Stimmen

Hier sind 8 Techniken; vielleicht funktioniert eine davon gut in Ihrem Fall.

0 Stimmen

(Das sind tatsächlich 5 Techniken -- einige waren keine Verbesserungen.)

450voto

Riedsio Punkte 9460

Ein großartiger Beitrag, der mehrere Fälle behandelt, von einfachen über Lücken bis hin zu nicht einheitlichen mit Lücken.

http://jan.kneschke.de/projects/mysql/order-by-rand/

Für den allgemeinsten Fall, hier ist wie es gemacht wird:

SELECT name
  FROM random AS r1 JOIN
       (SELECT CEIL(RAND() *
                     (SELECT MAX(id)
                        FROM random)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 1

Dies setzt voraus, dass die Verteilung der IDs gleich ist und dass es Lücken in der ID-Liste geben kann. Weitere fortgeschrittene Beispiele finden Sie im Artikel.

58 Stimmen

Ja, wenn Sie potenziell große Lücken in den IDs haben, ist die Wahrscheinlichkeit, dass Ihre niedrigsten IDs zufällig ausgewählt werden, viel geringer als bei Ihren hohen IDs. Tatsächlich ist die Wahrscheinlichkeit, dass die erste ID nach der größten Lücke ausgewählt wird, tatsächlich die höchste. Daher ist dies nach Definition nicht zufällig.

8 Stimmen

Wie bekommt man 10 verschiedene zufällige Zeilen? Muss man das Limit auf 10 setzen und dann 10 Mal mit mysqli_fetch_assoc($result) iterieren? Oder sind diese 10 Ergebnisse nicht unbedingt unterscheidbar?

18 Stimmen

Zufällig bedeutet für mich eine gleiche Chance für jedes Ergebnis. ;)

436voto

Preetam Purbia Punkte 5408
SELECT column FROM table
ORDER BY RAND()
LIMIT 10

Nicht die effiziente Lösung, funktioniert aber

176 Stimmen

ORDER BY RAND() ist relativ langsam

7 Stimmen

Mateusz - Beweis bitte, SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10 dauert 0,0010, ohne LIMIT 10 dauerte es 0,0012 (in dieser Tabelle 3500 Wörter).

39 Stimmen

@zeusakm 3500 Wörter sind nicht so viel; das Problem ist, dass es ab einem bestimmten Punkt explodiert, weil MySQL tatsächlich ALLE Datensätze sortieren muss, nachdem jeder gelesen wurde; sobald diese Operation die Festplatte erreicht, kann man den Unterschied spüren.

73voto

Ali Punkte 19874

Einfache Abfrage, die exzellente Leistung und mit Lücken funktioniert:

SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id

Diese Abfrage auf einer Tabelle mit 200K Zeilen dauert 0,08 Sekunden und die normale Version (SELECT * FROM tbl ORDER BY RAND() LIMIT 10) dauert auf meinem Rechner 0,35 Sekunden.

Dies geschieht schnell, da die Sortierphase nur die indizierte ID-Spalte verwendet. Dieses Verhalten kann im Explain-Plan eingesehen werden:

SELECT * FROM tbl ORDER BY RAND() LIMIT 10: Einfaches Erklärungsbild

SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id Bildbeschreibung eingeben

Gewichtete Version: https://stackoverflow.com/a/41577458/893432

5 Stimmen

Die abgeleitete Tabelle muss immer noch die gesamte Tabelle durchsuchen und sortieren.

0 Stimmen

Für mich ist es viel schneller in Millionen Daten

17voto

snippetsofcode Punkte 937

Ich erhalte schnelle Abfragen (ca. 0,5 Sekunden) mit einer langsamen CPU, indem ich 10 zufällige Zeilen in einer nicht zwischengespeicherten 400K-Register MySQL-Datenbank mit einer Größe von 2 GB auswähle. Hier ist mein Code: Schnelle Auswahl von zufälligen Zeilen in MySQL

$time= microtime_float();

$sql='SELECT COUNT(*) FROM pages';
$rquery= BD_Ejecutar($sql);
list($num_records)=mysql_fetch_row($rquery);
mysql_free_result($rquery);

$sql="SELECT id FROM pages WHERE RAND()*$num_records<20
   ORDER BY RAND() LIMIT 0,10";
$rquery= BD_Ejecutar($sql);
while(list($id)=mysql_fetch_row($rquery)){
    if($id_in) $id_in.=",$id";
    else $id_in="$id";
}
mysql_free_result($rquery);

$sql="SELECT id,url FROM pages WHERE id IN($id_in)";
$rquery= BD_Ejecutar($sql);
while(list($id,$url)=mysql_fetch_row($rquery)){
    logger("$id, $url",1);
}
mysql_free_result($rquery);

$time= microtime_float()-$time;

logger("num_records=$num_records",1);
logger("$id_in",1);
logger("Vergangene Zeit: $time segundos",1);

17 Stimmen

Angesichts meiner über 14 Millionen Datensätze in der Tabelle ist dies genauso langsam wie ORDER BY RAND()

6 Stimmen

@snippetsofcode In Ihrem Fall - 400k von Zeilen können Sie einfach "ORDER BY rand()" verwenden. Ihr Trick mit 3 Abfragen ist nutzlos. Sie können es umschreiben wie "SELECT id, url FROM pages WHERE id IN (SELECT id FROM pages ORDER BY rand() LIMIT 10)"

4 Stimmen

Deine Technik führt immer noch einen Tabellen-Scan durch. Verwende FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%';, um dies zu sehen.

14voto

zloctb Punkte 9390

Vom Buch :

Wählen Sie eine zufällige Zeile mit einem Offset aus

Noch eine Technik, die Probleme vermeidet, die in den vorherigen Alternativen gefunden wurden, besteht darin, die Zeilen im Datensatz zu zählen und eine Zufallszahl zwischen 0 und der Zählung zurückzugeben. Verwenden Sie dann diese Zahl als Offset beim Abfragen des Datensatzes

$rand = "SELECT ROUND(RAND() * (SELECT COUNT(*) FROM Bugs))";
$offset = $pdo->query($rand)->fetch(PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Bugs LIMIT 1 OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->execute( $offset );
$rand_bug = $stmt->fetch();

Verwenden Sie diese Lösung, wenn Sie nicht von zusammenhängenden Schlüsselwerten ausgehen können und sicherstellen müssen, dass jede Zeile die gleiche Chance hat, ausgewählt zu werden.

2 Stimmen

Für sehr große Tabellen wird SELECT count(*) langsam.

0 Stimmen

OFFSET muss über so viele Zeilen springen. Daher kostet diese "Lösung" im Durchschnitt 1,5*N, wobei N die Anzahl der Zeilen in der Tabelle ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X