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

Jan hat 152 Beiträge geschrieben

7 Kommentare zu “Zufälligen Datensatz auswählen

  1. Christian sagt:

    Beim ersten Beispiel müsste es Limit 10 heilten!

    Ansonsten Kompliment von einem stets lesenden Besucher für deinen Blog.

  2. Daniel sagt:

    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?

  3. Xian sagt:

    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 😉

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>