2016-09-07 3 views
2

Ich entschuldige mich, wenn dieses Thema zu dem Tod gemacht wurde, aber ich habe Schwierigkeiten mit der Auswahl einer zufälligen Zeile aus einer großen MySQL-Tabelle. Es ist eine Tabelle mit dem Namen photos und der Primärschlüssel ist PhotoID. Im Moment ist der ID-Bereich von ~ 1500 (aufgrund von Reihen, die beim Testen erstellt wurden, dann gelöscht) auf ~ 12000, mit einigen Lücken, und ich erwarte, dass er noch viel größer wird.MySQL - wahlfreie Zeile aus großer Tabelle

Während es ist schon relativ klein Ich habe mit:

SELECT PhotoID FROM photos 

... in einen PHP-Array $All_IDs, dann in PHP:

$RandomID = $All_IDs[mt_rand(0,count($All_IDs)-1)] 

dann:

SELECT /* other columns */ FROM photos WHERE PhotoID = $RandomID 

Das funktioniert gut, ich bekomme eine gute Auswahl an zufälligen Fotos, wenn ich es wiederhole. Allerdings denke ich nicht, dass es sehr effizient funktionieren wird, die gesamte PhotoID Spalte zu laden, um eine zufällige ID auszuwählen, dann eine andere Abfrage, um diese Aufzeichnung zu erhalten, besonders wenn ich mehrere auswähle. Genauso würde ich lieber nicht die gesamte Tabelle (alle Spalten) in ein Array auswählen, nur um eines auszuwählen. Mit der Hilfe von einigen anderen Stackoverflow Antworten kam ich mit dem nach Abschluß:

SELECT MIN(PhotoID) INTO @MinID FROM photos; 
SELECT MAX(PhotoID) INTO @MaxID FROM photos; 
SELECT PhotoID,/* other columns */ FROM photos WHERE PhotoID >= (@MinID + RAND() * (@MaxID - @MinID)) ORDER BY PhotoID LIMIT 0,1 

Ich dachte, das funktionieren würde, aber ich finde diese Abfrage mehrmals zu wiederholen nur mir eine kurze Spanne von IDs zu geben, in der 1500 - 1700 Bereich, wenn, wie oben, die IDs derzeit in Richtung 12.000 gehen. Ich kann nicht verstehen, warum das so ist?

Antwort

1

Ich vermute, Sie sehen, dass kleine Wertebereich, da RAND() (in der WHERE Klausel) wird für jede Zeile in der Tabelle ausgewertet. Und es ist viel wahrscheinlicher, dass PhotoID in der Zeile größer ist als ein niedrigerer Wert, der von dem Ausdruck auf der rechten Seite zurückgegeben wird. Daher gibt die Abfrage eine Menge zurück, die den niedrigeren PhotoID-Werten mehr Gewicht verleiht. Mit dem ORDER BY werden Sie die niedrigsten bekommen.

Um eine zufälligere Verteilung zu erhalten, müsste RAND() nur eine Zeit ausgewertet werden. Außerdem würde ich lieber nicht mehrere Abfragen ausführen (drei separate SELECT-Anweisungen), wenn ich die Arbeit in einer einzigen Anweisung und ohne benutzerdefinierte Variablen erledigen kann.

den Algorithmus zu implementieren es, wie Sie zu erreichen versuchen, sieht, würde ich es so etwas wie diesen Ansatz:

SELECT t.photoid 
     , ... 
    FROM photos t 
    JOIN (SELECT m.min_id + RAND() * (max_id - min_id) AS _rand 
      FROM (SELECT MIN(p.photoid) AS min_id 
         , MAX(p.photoid) AS max_id 
         FROM photos p 
        ) m 
     ) r 
     ON r._rand <= t.photoid 
    ORDER BY t.photoid 
    LIMIT 1 

In MySQL, die Inline-Ansichten (abgeleitete Tabellen in der MySQL-parlance) wird zuerst vor der äußeren Abfrage materialisiert werden. Da m eine einzelne Zeile zurückgibt, wird die Funktion RAND() in r nur einmal ausgewertet. Und dann wird der einzelne Wert aus dem Ausdruck in der äußeren Abfrage verwendet.

+0

Das ist ideal, danke ...Ich vermied 'ORDER BY RAND()', da ich wusste, dass 'RAND()' für jede Zeile aufgerufen würde, aber ich dachte, es würde nur einmal aufgerufen werden, wenn es Teil der 'WHERE'-Klausel wäre. Ich hätte auch nie daran gedacht, 'JOIN' zu verwenden. – Iain

+0

HINWEIS: Dieser Ansatz ist nicht unbedingt der beste Ansatz zum Zurückgeben einer zufälligen Zeile aus einer Gruppe. Ich habe versucht, den Grund für das Verhalten zu erklären, das bei der ursprünglichen Abfrage beobachtet wurde, und ein Beispiel für das Implementieren des scheinbar beabsichtigten Entwurfs der ursprünglichen Abfrage. (Die Verwendung von JOIN funktioniert in diesem Fall, weil die Inline-Ansicht "r" eine einzelne Zeile zurückgibt.) Wenn aus irgendeinem Grund mehrere Anweisungen wie im Original verwendet werden müssen, verschieben Sie die RAND() -Operation in eine separate Anweisung und Übergeben Sie einen * einzigen * statischen Wert in die eigentliche Abfrage. Was macht die Abfrage in dieser Antwort? – spencer7593

0

Diese Abfrage:

select * from photos order by rand() limit 1; 
+0

Beachten Sie, dass MySQL die Funktion RAND() für * jede * Zeile in der Tabelle auswertet. Und dann wird die resultierende Menge eine "Using filesort" -Operation erfordern, um die Zeile zu identifizieren, die den niedrigsten Wert von RAND() hat. Dieser Ansatz tendiert dazu, bei großen Mengen nicht gut zu skalieren. – spencer7593

+0

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

Verwandte Themen