Wie kann ich am besten eine Abfrage schreiben, die 10 Zeilen zufällig aus insgesamt 600.000 auswählt?
Zur Information, order by rand()
ist sehr langsam, wenn die Tabelle groß ist.
Wie kann ich am besten eine Abfrage schreiben, die 10 Zeilen zufällig aus insgesamt 600.000 auswählt?
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;
Das hilft ein bisschen für MyISAM, aber nicht für InnoDB (vorausgesetzt, dass id der gruppierte PRIMARY KEY
ist).
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.
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.
@max := (SELECT MAX(id) FROM table)
MAX(id)
jedes Mal zu berechnen, wenn Sie eine Zeile benötigen.SELECT FLOOR(rand() * @max) + 1 as rand)
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
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
Ich benötige 30 zufällige Datensätze, sollte ich also LIMIT 1
in der Abfrage überall auf LIMIT 30
ändern?
@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.
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.
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).
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.
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.
Wie, wenn Sie 10 Zeilen mit "LIMIT 10" erhalten möchten? Es scheint, dass die Möglichkeiten nicht einmal sind.
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.
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.)