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 ???? )


Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0