554 Stimmen

MySQL wähle 10 zufällige Zeilen aus 600.000 Zeilen schnell

Wie kann ich am besten eine Abfrage schreiben, die 10 Zeilen zufällig aus insgesamt 600.000 auswählt?

19 Stimmen

Hier sind 8 Techniken; vielleicht funktioniert eine davon gut in Ihrem Fall.

0 Stimmen

(Das sind tatsächlich 5 Techniken -- einige waren keine Verbesserungen.)

13voto

Muhammad Azeem Punkte 1100

Es ist eine sehr einfache und einfache Abfrage in einer Zeile.

SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;

30 Stimmen

Zur Information, order by rand() ist sehr langsam, wenn die Tabelle groß ist.

8 Stimmen

Manchmal wird das LANGSAME akzeptiert, wenn ich es EINFACH halten möchte.

0 Stimmen

Die Indizierung sollte auf die Tabelle angewendet werden, wenn sie groß ist.

10voto

user1931858 Punkte 9834

Wie man zufällige Zeilen aus einer Tabelle auswählt:

Von hier: Zufällige Zeilen in MySQL auswählen

Eine schnelle Verbesserung gegenüber dem "Tabellenscan" besteht darin, den Index zu verwenden, um zufällige IDs auszuwählen.

SELECT *
FROM random, (
        SELECT id AS sid
        FROM random
        ORDER BY RAND( )
        LIMIT 10
    ) tmp
WHERE random.id = tmp.sid;

1 Stimmen

Das hilft ein bisschen für MyISAM, aber nicht für InnoDB (vorausgesetzt, dass id der gruppierte PRIMARY KEY ist).

1 Stimmen

Die innere Abfrage führt einen vollständigen Tabellen-Scan durch und sortiert die Ergebnisse. Tatsächlich beinhalten die meisten, vielleicht sogar alle, Techniken in diesem Link einen vollständigen Scan.

6voto

Marki555 Punkte 5900

Alle besten Antworten wurden bereits gepostet (hauptsächlich diejenigen, die sich auf den Link http://jan.kneschke.de/projects/mysql/order-by-rand/ beziehen).

Ich möchte eine weitere Möglichkeit zur Beschleunigung hervorheben - Caching. Denken Sie darüber nach, warum Sie zufällige Zeilen benötigen. Wahrscheinlich möchten Sie einen zufälligen Beitrag or eine zufällige Anzeige auf einer Website anzeigen. Wenn Sie 100 Anfragen/s erhalten, ist es wirklich notwendig, dass jeder Besucher zufällige Zeilen erhält? In der Regel ist es völlig in Ordnung, diese X zufälligen Zeilen für 1 Sekunde (oder sogar 10 Sekunden) zu zwischenspeichern. Es spielt keine Rolle, ob 100 eindeutige Besucher in der gleichen Sekunde die gleichen zufälligen Beiträge erhalten, denn in der nächsten Sekunde werden weitere 100 Besucher eine andere Auswahl an Beiträgen erhalten.

Bei der Verwendung dieses Cachings können Sie auch einige der langsameren Lösungen für die zufälligen Daten verwenden, da sie unabhängig von Ihren Anfragen pro Sekunde nur einmal pro Sekunde aus MySQL abgerufen werden.

6voto

Hans Z Punkte 4534

Ich habe die Antwort von @Riedsio verbessert. Dies ist die effizienteste Abfrage, die ich auf einer großen, gleichmäßig verteilten Tabelle mit Lücken finden kann (getestet, um 1000 zufällige Zeilen aus einer Tabelle mit mehr als 2,6 Milliarden Zeilen zu erhalten).

(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)

Lassen Sie mich erklären, was passiert.

  1. @max := (SELECT MAX(id) FROM table)
    • Ich berechne und speichere das Maximum. Für sehr große Tabellen gibt es einen leichten Overhead, um MAX(id) jedes Mal zu berechnen, wenn Sie eine Zeile benötigen.
  2. SELECT FLOOR(rand() * @max) + 1 as rand)
    • Holt eine zufällige ID
  3. SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
    • Dies füllt die Lücken aus. Wenn Sie also zufällig eine Nummer in den Lücken wählen, wird einfach die nächste ID ausgewählt. Unter der Annahme, dass die Lücken gleichmäßig verteilt sind, sollte dies kein Problem darstellen.

Durch die Verwendung von Union können Sie alles in eine Abfrage einfügen und somit mehrere Abfragen vermeiden. Außerdem sparen Sie sich den Overhead bei der Berechnung von MAX(id). Je nach Anwendung kann dies viel oder sehr wenig ausmachen.

Beachten Sie, dass hier nur die IDs abgerufen und in zufälliger Reihenfolge abgerufen werden. Wenn Sie etwas Fortgeschritteneres tun möchten, empfehle ich Ihnen, Folgendes zu tun:

SELECT t.id, t.name -- etc, etc
FROM table t
INNER JOIN (
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
    (SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
) x ON x.id = t.id
ORDER BY t.id

0 Stimmen

Ich benötige 30 zufällige Datensätze, sollte ich also LIMIT 1 in der Abfrage überall auf LIMIT 30 ändern?

0 Stimmen

@Hassaan du solltest nicht, dass Ändern von LIMIT 1 auf LIMIT 30 würde dir 30 Datensätze in einer Reihe von einem zufälligen Punkt in der Tabelle bringen. Du solltest stattdessen 30 Kopien des (SELECT id FROM .... Teils in der Mitte haben.

0 Stimmen

Ich habe es versucht, aber es scheint nicht effizienter zu sein als die Antwort von Riedsio. Ich habe es mit 500 Hits pro Sekunde auf der Seite versucht, die PHP 7.0.22 und MariaDB auf CentOS 7 verwendet, und mit der Antwort von Riedsio habe ich 500+ zusätzliche erfolgreiche Antworten erhalten als mit Ihrer Antwort.

5voto

Codemonkey Punkte 4082

Ich habe mir alle Antworten angesehen, und ich glaube nicht, dass jemand diese Möglichkeit überhaupt erwähnt, und ich bin mir nicht sicher, warum.

Wenn Sie größte Einfachheit und Geschwindigkeit bei geringen Kosten wünschen, scheint es mir sinnvoll, für jede Zeile in der DB eine Zufallszahl zu speichern. Erstellen Sie einfach eine zusätzliche Spalte, random_number, und setzen Sie den Standardwert auf RAND(). Erstellen Sie einen Index auf dieser Spalte.

Dann, wenn Sie eine Zeile abrufen möchten, generieren Sie eine Zufallszahl in Ihrem Code (PHP, Perl, was auch immer) und vergleichen Sie diese mit der Spalte.

SELECT FROM tbl WHERE random_number >= :random LIMIT 1

Ich vermute, obwohl es sehr ordentlich für eine einzelne Zeile ist, müssten Sie es für zehn Zeilen wie vom OP gefragt zehnmal separat aufrufen (oder einen clevere Änderung vornehmen, die mir sofort entgeht).

1 Stimmen

Dies ist tatsächlich ein sehr schöner und effizienter Ansatz. Der einzige Nachteil ist, dass du Platz für Geschwindigkeit getauscht hast, was meiner Meinung nach fair ist.

0 Stimmen

Vielen Dank. Ich hatte ein Szenario, in dem die Haupttabelle, aus der ich eine zufällige Zeile haben wollte, 5 Millionen Zeilen hatte und ziemlich viele Joins. Nachdem ich die meisten Ansätze in dieser Frage ausprobiert hatte, war dies der Trick, auf den ich mich geeinigt habe. Eine zusätzliche Spalte war für mich ein sehr lohnender Kompromiss.

0 Stimmen

Wie, wenn Sie 10 Zeilen mit "LIMIT 10" erhalten möchten? Es scheint, dass die Möglichkeiten nicht einmal sind.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X