Gast: Zufalls-Datensatz nicht zufällig

Hallo,

in MySQL selektiere ich Datensätze und möchte aus dieser Menge zufällig einen auslesen. Unter Zufall verstehe ich, dass bei einer großen Zahl alle mal vorkommen, aber bei meinem Verfahren drängen sich drei oder vier immer in den Vordergrund, andere sind nie dabei:

SELECT  
 ort1.id      ORT  
...  
FROM   daten trm1  
      ,orte ort1  
WHERE  trm1.owner_id  = 1  
AND    trm1.emb_object IS NOT NULL  
AND    trm1.emb_object > ' '  
AND    trm1.intern_kz  = 0  
AND    trm1.tag       >= CURDATE()  
AND    ort1.id         = trm1.ort_id  
ORDER BY RAND() LIMIT 1

Wenn die letzte Zeile entfällt, habe ich 62 Treffer.

Was ist falsch?

Gruß, Gast

  1. in MySQL selektiere ich Datensätze und möchte aus dieser Menge zufällig einen auslesen. Unter Zufall verstehe ich, dass bei einer großen Zahl alle mal vorkommen, aber bei meinem Verfahren drängen sich drei oder vier immer in den Vordergrund, andere sind nie dabei:

    Was ist falsch?

    Dein Wissen um "Zufall" in der der Datenverarbeitung - Pseudozufallszahlen sind leider selten wirklich zufällig :)

    Ggf. ist aber einfach nur der Query-Cache vom SQL-Server dagegen.

    SQL_NO_CACHE

    1. Dein Wissen um "Zufall" in der der Datenverarbeitung - Pseudozufallszahlen sind leider selten wirklich zufällig :)

      RAND() is not meant to be a perfect random generator. It is a fast way to generate random numbers on demand that is portable between platforms for the same MySQL version.

      Mit anderen Worten: nicht wirklich zufällig, aber dafür konsistent "nicht zufällig" :)

      1. Danke, habe SQL_NO_CACHE eingebaut. Schaun mer mal die näxten Tage.

        Sind die Zufälle mit PHP besser? Alle 62 Treffer selektieren und PHP eine Zahl von 1 .. 62 generieren lassen?

        Gruß, Gast

        1. Sind die Zufälle mit PHP besser?

          Das kommt drauf an, wie du das definierst - Zufallszahlen mit PHP sind jedenfalls "zufälliger" als die von MySQL, allerdings mit dem Nachteil, dass sie Abweichungen auf verschiedenen Plattformen haben.

          Im Core kannst du dir das hier ansehen:
          http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3_10/ext/standard/rand.c?revision=323023&view=markup

          Bzw. hier, wie die gewöhnliche rand-Funktion arbeitet:
          http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3_10/ext/standard/php_rand.h?revision=323023&view=markup

          Hier sieht man z.B. sehr deutlich, dass unter Windows die Zufallszahl anders erzeugt wird als unter anderen Plattformen.

          Alle 62 Treffer selektieren und PHP eine Zahl von 1 .. 62 generieren lassen?

          Musst du ausprobieren was schneller geht und zufälliger ist:

          • alle selektieren, den Rückgabewert durchmischen und den ersten rausnehmen.
          • alle selektieren und einen bestimmten Index (aus Zufallszahl) rausnehmen
          • alle zählen und dann einen gezielten mit (Limit) aufgrund einer Zufallszahl rausnehmen (zwei Abfragen)

          Zu empfehlen ist im Fall von PHP jedenfalls mt_rand() und nicht rand(), da zweiteres bei häufigen aufruf schon entsprechend Muster erkennen lässt.

          Am einfachsten ist das nachzuvollziehen, wenn man einfach in einer Schleife Zufallszahlen ausgeben lässt und diese dann in einer Kurve darstellt oder die häufigkeit auswertet.

  2. aber bei meinem Verfahren drängen sich drei oder vier immer in den Vordergrund, andere sind nie dabei

    Bei mir drängt sich bei ORDER BY RAND() immer der erste Datensatz nach vorn bzw. es kommt mir zumindest so vor. Ich hab das dann so gelöst, dass ich LIMIT 50,1 (je nachdem, wie viele Datensätze in der Tabelle sind... irgendwas mittendrin) nehme. Dadurch fällt mir sowas nicht mehr auf...

    Gruß,
    Andreas

  3. Hi,

    in MySQL selektiere ich Datensätze und möchte aus dieser Menge zufällig einen auslesen.

    SELECT

    ort1.id      ORT
    ...
    FROM   daten trm1
          ,orte ort1
    WHERE  trm1.owner_id  = 1
    AND    trm1.emb_object IS NOT NULL
    AND    trm1.emb_object > ' '
    AND    trm1.intern_kz  = 0
    AND    trm1.tag       >= CURDATE()
    AND    ort1.id         = trm1.ort_id
    ORDER BY RAND() LIMIT 1

      
    Du wirst ggf. noch ein anderes Problem bekommen: deine Query ist potentiell recht langsam.  
    Das liegt daran, dass du deine Ergebnismenge komplett berechnen musst, dann komplett (de-)sortieren um dann genau einen Datensatz zu selektieren.  
    Wenn du weißt, dass keine Lücken in deinen IDs existieren, dann könntest du anhand einer Gleichverteilung darüber eine zufällige ID im Bereich {id\_min, ..., id\_max} selektieren und dann den kompletten Datensatz aus der DB abholen. Wenn es nur wenige Lücken gibt, dann könntest du beim Auftreffen einer Lücke einfach nochmal eine Zufallszahl ziehen, wenn es mehr Lücken werden, wird das aber auch langsam weil du dann viele misses bekommst.  
      
    Gibt noch ein paar Möglichkeiten mehr (z.B. nur IDs selektieren, PHP's rand() verwenden und dann mit der ID den ganzen Datensatz holen), aber man findet dazu einiges bei google.  
      
    Bis die Tage,  
    Matti
    
    -- 
    [Webapplikationen in C++ entwickeln](http://tntnet.org/)