
Zufälligen Datensatz auswählen
Manchmal möchte man einen oder mehrere Datensätze einer Tabelle zufällig auswählen. Bei normalen Sortierungen kann man einen Index zur Bschleunigung setzen, aber bei RAND() funktioniert das nicht. Das kann beispielsweise sein, wenn viele Artikel zur Wahl stehen und allen mal die Chance gegeben werden soll auf der Startseite zu erscheinen. Also können wir ja einfach folgende Abfrage ausführen:
SELECT artikelname FROM tabelle ORDER BY RAND() LIMIT 10;
Das gibt uns 10 Datensätze zurück, die zufällig ausgewählt wurden. Allerdings dauert es eine halbe Ewigkeit, weil für jeden Datensatz der Tabelle eine Zufallszahl erstellt werden muss. Eine Zufallszahl zu erstellen dauert zwar nicht lange – wenn man allerdings mehrere Tausend Einträge in der DB hat, summiert sich das schnell zu einigen Sekunden. Wie können wir das Problem nun umgehen?
Die Zufallszahl sollte nur einmal generiert werden müssen. Und das ist dann unser Startpunkt für das Selektieren. In SQL können wir das so umsetzen:
SELECT artikelname FROM tabelle JOIN ( SELECT ( RAND( ) * ( SELECT MAX( ID ) FROM tabelle ) ) AS randID ) AS randTable WHERE tabelle.ID=randTable.randID LIMIT 1;
Durch den JOIN wird eine Zufallszal erzeugt, die zwischen 0 und COUNT(*) der Tabelle liegt. Möchte man nur einen Datensatz zufällig wählen, ist diese Variante die schnellste.
Manchmal möchte man aber auch mehrere Datensätze wählen. Auch das ist möglich, allerdings nicht mehr so sehr zufällig.
SELECT artikelname FROM tabelle JOIN ( SELECT ( ABS( RAND( ) * ( SELECT MAX( ID ) FROM tabelle )-10 ) ) AS randID ) AS randTable WHERE tabelle.ID>=randTable.randID LIMIT 10;
Es wurde die ABS-Funktion hinzugefügt, da wir nun von der ermittelten Zufallszahl die Anzahl der zu selektierenden Datensätze abziehen. Ansonsten kann es nämlich passieren, dass RAND() nahe 1 ist und somit die randID recht nah an COUNT(*) der Tabelle liegt. Das würde im Extremfall bedeuten, dass wir keine 10 Datensätze bekommen würden, weil die Zufallszahl zu groß ist und nur noch weniger als 10 IDs über der randID sind.
Aber wei gesagt: Diese Methode fischt nicht 10 zufällige Datensätze heraus, sondern 10 aufeinanderfolgende beginnend ab einer zufälligen ID.
Für schnelles und zufälliges Selektieren liefert Jan Kneschke einen Ansatz über eine zusätzliche Mappingtabelle mit Triggern. Wer sich da weiter einarbeiten möchte, sollte sich das durchlesen, ich möchte allerdings die Komplexität hier nicht überstrapazieren (das soll nicht heißen, dass ich nicht verstehe, was Jan schreibt
)














Christian sagt
am 10. Mai 2007 @ 13:43
Beim ersten Beispiel müsste es Limit 10 heilten!
Ansonsten Kompliment von einem stets lesenden Besucher für deinen Blog.
admin sagt
am 10. Mai 2007 @ 13:56
Danke für den Hinweis. Habe es geändert.
Daniel sagt
am 15. Mai 2007 @ 14:00
Hast du schonmal versucht zehn Zufallszahlen zwischen 1 und der höchsten ID zu erzeugen und dann in einem zweiten Statement einfach nur noch diese 10 abzugreifen?
Ähnelt deiner JOIN Variante, weiß nicht welche schneller ist?
admin sagt
am 16. Mai 2007 @ 08:35
Weiß im Moment nicht genau, wie Du das meinst. Hab ein Thema im Forum eröffnet. Würde mich freuen, wenn Du dort Deine Lösung vorstellen könntest. Beitrag ist zu finden unter Zufälligen Datensatz auswählen
PHP Performance » Performanter Zufall - gibts das? sagt
am 18. Mai 2007 @ 10:24
[...] Problem des zufälligen Selektierens von Datensätzen nochmal betrachten, da die im Beitrag Zufälligen Datensatz auswählen gefundene Lösung nicht wirklich akzeptabel ist, da sie nicht zufällig ist. Bei aller [...]
[PHP] Auslosverfahren über (?) $rand - jswelt - Forum (Javascript, PHP, MySQL, AJAX, Webdesign) sagt
am 27. Oktober 2007 @ 13:20
[...] auszuwählen und dann entsprechend auszugeben wenn ich dich richtig verstanden habe. hier ein Tip: Zufälligen Datensatz auswählen » Beitrag » PHP Performance schreiben musste aber wohl selbst. bine Listen-Navigationen apicula art-stylers.de Schick [...]
Xian sagt
am 17. Januar 2010 @ 16:08
Das ist ein interessanter Ansatz.
Momentan bin ich glücklicherweise noch weit davon entfernt, dass meine Abfragen mit RAND() zu langsam sind. Aber wenn meine Tabelle irgendwann einmal zu groß wird, komme ich noch einmal auf dich zurück