Antworten:
Ein großartiger Beitrag, der mehrere Fälle bearbeitet, von einfach über Lücken bis hin zu ungleichmäßigen Lücken.
http://jan.kneschke.de/projects/mysql/order-by-rand/
Im allgemeinsten Fall gehen Sie folgendermaßen vor:
SELECT name
FROM random AS r1 JOIN
(SELECT CEIL(RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1
Dies setzt voraus, dass die Verteilung der IDs gleich ist und dass es Lücken in der ID-Liste geben kann. Weitere Informationen finden Sie im Artikel
mysqli_fetch_assoc($result)
? Oder sind diese 10 Ergebnisse nicht unbedingt unterscheidbar?
SELECT column FROM table
ORDER BY RAND()
LIMIT 10
Nicht die effiziente Lösung, funktioniert aber
ORDER BY RAND()
ist relativ langsam
SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10
dauert 0,0010, ohne LIMIT 10 dauerte es 0,0012 (in dieser Tabelle 3500 Wörter).
Einfache Abfrage, die eine hervorragende Leistung aufweist und mit Lücken arbeitet :
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id
Diese Abfrage für eine 200K-Tabelle dauert 0,08 Sekunden und die normale Version (SELECT * FROM tbl ORDER BY RAND () LIMIT 10) dauert 0,35 Sekunden auf meinem Computer .
Dies ist schnell, da in der Sortierphase nur die indizierte ID-Spalte verwendet wird. Sie können dieses Verhalten in der Erklärung sehen:
SELECT * FROM tbl ORDER BY RAND () LIMIT 10:
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND () LIMIT 10) als t2 ON t1.id = t2.id.
Gewichtete Version : https://stackoverflow.com/a/41577458/893432
Ich erhalte schnelle Abfragen (ca. 0,5 Sekunden) mit einer langsamen CPU und wähle 10 zufällige Zeilen in einer nicht zwischengespeicherten 2-GB-Größe der MySQL-Datenbank mit 400 KB aus. Siehe hier meinen Code: Schnelle Auswahl von zufälligen Zeilen in MySQL
<?php
$time= microtime_float();
$sql='SELECT COUNT(*) FROM pages';
$rquery= BD_Ejecutar($sql);
list($num_records)=mysql_fetch_row($rquery);
mysql_free_result($rquery);
$sql="SELECT id FROM pages WHERE RAND()*$num_records<20
ORDER BY RAND() LIMIT 0,10";
$rquery= BD_Ejecutar($sql);
while(list($id)=mysql_fetch_row($rquery)){
if($id_in) $id_in.=",$id";
else $id_in="$id";
}
mysql_free_result($rquery);
$sql="SELECT id,url FROM pages WHERE id IN($id_in)";
$rquery= BD_Ejecutar($sql);
while(list($id,$url)=mysql_fetch_row($rquery)){
logger("$id, $url",1);
}
mysql_free_result($rquery);
$time= microtime_float()-$time;
logger("num_records=$num_records",1);
logger("$id_in",1);
logger("Time elapsed: <b>$time segundos</b>",1);
?>
ORDER BY RAND()
FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%';
, um es zu sehen.
ORDER BY RAND()
besteht darin, dass nur die IDs (nicht die vollständigen Zeilen) sortiert werden, sodass die temporäre Tabelle kleiner ist, aber dennoch alle sortiert werden muss.
Es ist eine sehr einfache und einzeilige Abfrage.
SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;
order by rand()
Aus dem Buch:
Wählen Sie eine zufällige Zeile mit einem Versatz
Eine weitere Technik, die Probleme in den vorhergehenden Alternativen vermeidet, besteht darin, die Zeilen im Datensatz zu zählen und eine Zufallszahl zwischen 0 und der Anzahl zurückzugeben. Verwenden Sie diese Nummer dann als Offset, wenn Sie den Datensatz abfragen
<?php
$rand = "SELECT ROUND(RAND() * (SELECT COUNT(*) FROM Bugs))";
$offset = $pdo->query($rand)->fetch(PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Bugs LIMIT 1 OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->execute( $offset );
$rand_bug = $stmt->fetch();
Verwenden Sie diese Lösung, wenn Sie keine zusammenhängenden Schlüsselwerte annehmen können und sicherstellen müssen, dass jede Zeile eine gleichmäßige Chance hat, ausgewählt zu werden.
SELECT count(*)
wird langsam.
So wählen Sie zufällige Zeilen aus einer Tabelle aus:
Von hier aus: Wählen Sie zufällige Zeilen in MySQL aus
Eine schnelle Verbesserung gegenüber "Tabellenscan" besteht darin, den Index zum Abrufen zufälliger IDs zu verwenden.
SELECT *
FROM random, (
SELECT id AS sid
FROM random
ORDER BY RAND( )
LIMIT 10
) tmp
WHERE random.id = tmp.sid;
PRIMARY KEY
).
Wenn Ihre Schlüssel keine Lücken aufweisen und alle numerisch sind, können Sie Zufallszahlen berechnen und diese Zeilen auswählen. Dies wird aber wahrscheinlich nicht der Fall sein.
Eine Lösung wäre also die folgende:
SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1
Dies stellt im Grunde sicher, dass Sie eine Zufallszahl im Bereich Ihrer Tasten erhalten und dann die nächstbeste auswählen, die größer ist. Sie müssen dies 10 Mal tun.
Dies ist jedoch NICHT wirklich zufällig, da Ihre Schlüssel höchstwahrscheinlich nicht gleichmäßig verteilt werden.
Es ist wirklich ein großes Problem und nicht einfach zu lösen, um alle Anforderungen zu erfüllen. Rand () von MySQL ist das Beste, was Sie bekommen können, wenn Sie wirklich 10 zufällige Zeilen wollen.
Es gibt jedoch eine andere Lösung, die schnell ist, aber auch einen Kompromiss in Bezug auf Zufälligkeit aufweist, aber möglicherweise besser zu Ihnen passt. Lesen Sie hier darüber: Wie kann ich die ORDER BY RAND () - Funktion von MySQL optimieren?
Die Frage ist, wie zufällig Sie es brauchen.
Können Sie uns etwas mehr erklären, damit ich Ihnen eine gute Lösung geben kann?
Zum Beispiel hatte ein Unternehmen, mit dem ich zusammengearbeitet habe, eine Lösung, bei der es extrem schnell um absolute Zufälligkeit ging. Am Ende wurde die Datenbank mit Zufallswerten gefüllt, die absteigend ausgewählt und anschließend wieder auf andere Zufallswerte gesetzt wurden.
Wenn Sie kaum jemals aktualisieren, können Sie auch eine inkrementelle ID eingeben, damit Sie keine Lücken haben und vor der Auswahl nur zufällige Schlüssel berechnen können ... Dies hängt vom Anwendungsfall ab!
Id
und alle Ihre zufälligen Abfragen geben Ihnen diese zurück Id
.
FLOOR(RAND()*MAX(id))
ist voreingenommen in Richtung der Rückgabe größerer IDs.
Ich brauchte eine Abfrage, um eine große Anzahl zufälliger Zeilen aus einer ziemlich großen Tabelle zurückzugeben. Das habe ich mir ausgedacht. Erhalten Sie zuerst die maximale Datensatz-ID:
SELECT MAX(id) FROM table_name;
Ersetzen Sie diesen Wert dann durch:
SELECT * FROM table_name WHERE id > FLOOR(RAND() * max) LIMIT n;
Dabei ist max die maximale Datensatz-ID in der Tabelle und n die Anzahl der Zeilen, die Sie in Ihrer Ergebnismenge haben möchten. Die Annahme ist, dass es keine Lücken in den Datensatz-IDs gibt, obwohl ich bezweifle, dass dies das Ergebnis beeinflussen würde, wenn es solche gäbe (habe es aber nicht versucht). Ich habe diese gespeicherte Prozedur auch allgemeiner erstellt. Übergeben Sie den Tabellennamen und die Anzahl der zurückzugebenden Zeilen. Ich verwende MySQL 5.5.38 unter Windows 2008, 32 GB, Dual 3 GHz E5450 und in einer Tabelle mit 17.361.264 Zeilen ist es mit ~ 0,03 Sekunden / ~ 11 Sekunden ziemlich konsistent, um 1.000.000 Zeilen zurückzugeben. (Die Zeiten stammen aus MySQL Workbench 6.1. Sie können in der zweiten select-Anweisung auch CEIL anstelle von FLOOR verwenden, je nach Ihren Vorlieben.)
DELIMITER $$
USE [schema name] $$
DROP PROCEDURE IF EXISTS `random_rows` $$
CREATE PROCEDURE `random_rows`(IN tab_name VARCHAR(64), IN num_rows INT)
BEGIN
SET @t = CONCAT('SET @max=(SELECT MAX(id) FROM ',tab_name,')');
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @t = CONCAT(
'SELECT * FROM ',
tab_name,
' WHERE id>FLOOR(RAND()*@max) LIMIT ',
num_rows);
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$
dann
CALL [schema name].random_rows([table name], n);
Die besten Antworten wurden bereits veröffentlicht (hauptsächlich diejenigen, die auf den Link http://jan.kneschke.de/projects/mysql/order-by-rand/ verweisen ).
Ich möchte eine andere Möglichkeit der Beschleunigung aufzeigen - das Caching . Überlegen Sie, warum Sie zufällige Zeilen benötigen. Wahrscheinlich möchten Sie einen zufälligen Beitrag oder eine zufällige Anzeige auf einer Website anzeigen. Wenn Sie 100 Anforderungen / s erhalten, ist es wirklich erforderlich, dass jeder Besucher zufällige Zeilen erhält? Normalerweise ist es völlig in Ordnung, diese X zufälligen Zeilen 1 Sekunde (oder sogar 10 Sekunden) zwischenzuspeichern. Es spielt keine Rolle, ob 100 eindeutige Besucher in derselben Sekunde dieselben zufälligen Beiträge erhalten, da in der nächsten Sekunde weitere 100 Besucher unterschiedliche Beiträge erhalten.
Wenn Sie dieses Caching verwenden, können Sie auch einige der langsameren Lösungen zum Abrufen der Zufallsdaten verwenden, da diese unabhängig von Ihren Anforderungen nur einmal pro Sekunde von MySQL abgerufen werden.
Ich habe die Antwort von @Riedsio verbessert. Dies ist die effizienteste Abfrage, die ich für eine große, gleichmäßig verteilte Tabelle mit Lücken finden kann (getestet, um 1000 zufällige Zeilen aus einer Tabelle mit> 2,6B 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 auspacken, was los ist.
@max := (SELECT MAX(id) FROM table)
MAX(id)
jedes Mal, wenn Sie eine Zeile benötigenSELECT FLOOR(rand() * @max) + 1 as rand)
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
Durch die Vereinigung können Sie alles in eine Abfrage einpassen, sodass Sie nicht mehrere Abfragen ausführen müssen. Sie können damit auch den Aufwand für die Berechnung sparen MAX(id)
. Abhängig von Ihrer Anwendung kann dies sehr oder sehr wenig bedeuten.
Beachten Sie, dass dadurch nur die IDs und in zufälliger Reihenfolge abgerufen werden. Wenn Sie etwas Fortgeschritteneres tun möchten, empfehle ich Ihnen Folgendes:
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
LIMIT 1
um LIMIT 30
in Abfrage überall
LIMIT 1
zu LIMIT 30
30 Datensätze in einer Reihe von einem zufälligen Punkt in der Tabelle erhalten würde. Sie sollten stattdessen 30 Kopien des (SELECT id FROM ....
Teils in der Mitte haben.
Riedsio
antworten. Ich habe mit 500 pro Sekunde Treffer auf die Seite mit PHP 7.0.22 und MariaDB auf Centos 7 versucht. Mit der Riedsio
Antwort habe ich mehr als 500 zusätzliche erfolgreiche Antworten erhalten als Ihre Antwort.
Ich habe diese http://jan.kneschke.de/projects/mysql/order-by-rand/ von Riedsio verwendet (ich habe den Fall einer gespeicherten Prozedur verwendet, die einen oder mehrere zufällige Werte zurückgibt):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
INSERT INTO rands
SELECT r1.id
FROM random AS r1 JOIN
(SELECT (RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1;
SET cnt = cnt - 1;
END LOOP loop_me;
In dem Artikel löst er das Problem von Lücken in IDs, die nicht so zufällige Ergebnisse verursachen, indem er eine Tabelle verwaltet (unter Verwendung von Triggern usw. ... siehe Artikel); Ich löse das Problem, indem ich der Tabelle eine weitere Spalte hinzufüge, die mit zusammenhängenden Zahlen gefüllt ist, beginnend mit 1 (hinzufüge Bearbeiten: Diese Spalte wird der temporären Tabelle hinzugefügt, die zur Laufzeit von der Unterabfrage erstellt wurde, wirkt sich nicht auf Ihre permanente Tabelle aus):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
SET @no_gaps_id := 0;
INSERT INTO rands
SELECT r1.id
FROM (SELECT id, @no_gaps_id := @no_gaps_id + 1 AS no_gaps_id FROM random) AS r1 JOIN
(SELECT (RAND() *
(SELECT COUNT(*)
FROM random)) AS id)
AS r2
WHERE r1.no_gaps_id >= r2.id
ORDER BY r1.no_gaps_id ASC
LIMIT 1;
SET cnt = cnt - 1;
END LOOP loop_me;
In dem Artikel kann ich sehen, dass er große Anstrengungen unternommen hat, um den Code zu optimieren. Ich habe keine Ahnung, ob / wie sehr sich meine Änderungen auf die Leistung auswirken, aber es funktioniert sehr gut für mich.
@no_gaps_id
kein Index verwendet werden kann, so dass , wenn man sich anschaut , EXPLAIN
für Ihre Anfrage, Sie haben Using filesort
und Using where
(ohne Index) für die Unterabfragen, im Gegensatz zu der ursprünglichen Abfrage.
Hier ist ein Game Changer, der für viele hilfreich sein kann.
Ich habe eine Tabelle mit 200.000 Zeilen und sequentiellen IDs. Ich musste N zufällige Zeilen auswählen. Daher habe ich mich dafür entschieden, zufällige Werte basierend auf der größten ID in der Tabelle zu generieren. Ich habe dieses Skript erstellt, um herauszufinden, welche Operation die schnellste ist:
logTime();
query("SELECT COUNT(id) FROM tbl");
logTime();
query("SELECT MAX(id) FROM tbl");
logTime();
query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1");
logTime();
Die Ergebnisse sind:
36.8418693542479
ms0.241041183472
ms0.216960906982
msBasierend auf diesen Ergebnissen ist order desc die schnellste Operation, um die maximale ID zu erhalten.
Hier ist meine Antwort auf die Frage:
SELECT GROUP_CONCAT(n SEPARATOR ',') g FROM (
SELECT FLOOR(RAND() * (
SELECT id FROM tbl ORDER BY id DESC LIMIT 1
)) n FROM tbl LIMIT 10) a
...
SELECT * FROM tbl WHERE id IN ($result);
Zu Ihrer Information: Um 10 zufällige Zeilen aus einer 200k-Tabelle zu erhalten, habe ich 1,78 ms gebraucht (einschließlich aller Operationen auf der PHP-Seite).
LIMIT
geringfügig erhöhen - Sie können Duplikate erhalten.
Dies ist super schnell und 100% zufällig, auch wenn Sie Lücken haben.
x
der verfügbaren ZeilenSELECT COUNT(*) as rows FROM TABLE
a_1,a_2,...,a_10
zwischen 0 undx
SELECT * FROM TABLE LIMIT 1 offset a_i
für i = 1, ..., 10Ich habe diesen Hack in dem Buch SQL Antipatterns von Bill Karwin gefunden .
SELECT column FROM table ORDER BY RAND() LIMIT 10
in O (nlog (n)) befindet. Ja, dies ist die schnellste Lösung und funktioniert für jede Verteilung von IDs.
x
. Ich würde argumentieren, dass dies keine zufällige Generation von 10 Zeilen ist. In meiner Antwort müssen Sie die Abfrage in Schritt drei zehnmal ausführen, dh man erhält nur eine Zeile pro Ausführung und muss sich keine Sorgen machen, wenn der Offset am Ende der Tabelle steht.
Kombinieren Sie die Antwort von @redsio mit einer temporären Tabelle (600K sind nicht so viel):
DROP TEMPORARY TABLE IF EXISTS tmp_randorder;
CREATE TABLE tmp_randorder (id int(11) not null auto_increment primary key, data_id int(11));
INSERT INTO tmp_randorder (data_id) select id from datatable;
Und dann nimm eine Version von @redsios Antwort:
SELECT dt.*
FROM
(SELECT (RAND() *
(SELECT MAX(id)
FROM tmp_randorder)) AS id)
AS rnd
INNER JOIN tmp_randorder rndo on rndo.id between rnd.id - 10 and rnd.id + 10
INNER JOIN datatable AS dt on dt.id = rndo.data_id
ORDER BY abs(rndo.id - rnd.id)
LIMIT 1;
Wenn der Tisch groß ist, können Sie den ersten Teil sieben:
INSERT INTO tmp_randorder (data_id) select id from datatable where rand() < 0.01;
Version: Sie könnten die Tabelle behalten tmp_randorder
persistent halten und als datatable_idlist bezeichnen. Erstellen Sie diese Tabelle in bestimmten Intervallen (Tag, Stunde) neu, da sie auch Löcher bekommt. Wenn Ihr Tisch wirklich groß wird, können Sie auch Löcher nachfüllen
Wählen Sie l.data_id als Ganzes aus der Liste datatable_id aus. l left join datatable dt on dt.id = l.data_id wobei dt.id null ist.
Version: Geben Sie Ihrem Dataset eine random_sortorder-Spalte entweder direkt in datatable oder in einer persistenten zusätzlichen Tabelle datatable_sortorder
. Indizieren Sie diese Spalte. Generieren Sie einen Zufallswert in Ihrer Anwendung (ich werde es nennen $rand
).
select l.*
from datatable l
order by abs(random_sortorder - $rand) desc
limit 1;
Diese Lösung unterscheidet die 'Kantenzeilen' mit der höchsten und der niedrigsten random_sortorder, also ordnen Sie sie in Intervallen (einmal am Tag) neu an.
Eine andere einfache Lösung wäre, die Zeilen zu ordnen und eine davon zufällig abzurufen. Mit dieser Lösung müssen Sie keine 'Id'-basierte Spalte in der Tabelle haben.
SELECT d.* FROM (
SELECT t.*, @rownum := @rownum + 1 AS rank
FROM mytable AS t,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM mytable))) AS n
) d WHERE rank >= @cnt LIMIT 10;
Sie können den Grenzwert nach Bedarf ändern, um auf so viele Zeilen zuzugreifen, wie Sie möchten. Dies sind jedoch meistens aufeinanderfolgende Werte.
Wenn Sie jedoch keine aufeinanderfolgenden Zufallswerte möchten, können Sie eine größere Stichprobe abrufen und zufällig auswählen. etwas wie ...
SELECT * FROM (
SELECT d.* FROM (
SELECT c.*, @rownum := @rownum + 1 AS rank
FROM buildbrain.`commits` AS c,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM buildbrain.`commits`))) AS rnd
) d
WHERE rank >= @cnt LIMIT 10000
) t ORDER BY RAND() LIMIT 10;
Eine Möglichkeit, die ich ziemlich gut finde, wenn es eine automatisch generierte ID gibt, ist die Verwendung des Modulo-Operators '%'. Wenn Sie beispielsweise 10.000 zufällige Datensätze von 70.000 benötigen, können Sie dies vereinfachen, indem Sie sagen, dass Sie 1 von 7 Zeilen benötigen. Dies kann in dieser Abfrage vereinfacht werden:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0;
Wenn das Ergebnis der Division der Zielzeilen durch die verfügbare Gesamtzahl keine Ganzzahl ist, stehen Ihnen einige zusätzliche Zeilen zur Verfügung, als Sie angefordert haben. Fügen Sie daher eine LIMIT-Klausel hinzu, um die Ergebnismenge wie folgt zu kürzen:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0
LIMIT 10000;
Dies erfordert einen vollständigen Scan, ist jedoch schneller als ORDER BY RAND und meiner Meinung nach einfacher zu verstehen als andere in diesem Thread erwähnte Optionen. Auch wenn das System, das in die Datenbank schreibt, Sätze von Zeilen in Stapeln erstellt, erhalten Sie möglicherweise kein so zufälliges Ergebnis, wie Sie es erwartet haben.
Wenn Sie einen zufälligen Datensatz möchten (unabhängig davon, ob zwischen den IDs Lücken bestehen):
PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
FLOOR(RAND() * COUNT(*))
FROM `table_name`);
EXECUTE stmt USING @count;
Ich habe alle Antworten durchgesehen, und ich glaube, niemand erwähnt diese Möglichkeit überhaupt, und ich bin mir nicht sicher, warum.
Wenn Sie äußerste Einfachheit und Geschwindigkeit zu geringen Kosten wünschen, erscheint es mir sinnvoll, eine Zufallszahl für jede Zeile in der Datenbank zu speichern. Erstellen Sie einfach eine zusätzliche Spalte random_number
und setzen Sie die Standardeinstellung auf RAND()
. Erstellen Sie einen Index für diese Spalte.
Wenn Sie dann 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 denke, obwohl es für eine einzelne Zeile sehr ordentlich ist, müssten Sie es für zehn Zeilen wie das OP zehn Mal einzeln aufrufen (oder sich eine clevere Optimierung einfallen lassen, die mir sofort entgeht).
Das Folgende sollte schnell, unvoreingenommen und unabhängig von der ID-Spalte sein. Es kann jedoch nicht garantiert werden, dass die Anzahl der zurückgegebenen Zeilen mit der Anzahl der angeforderten Zeilen übereinstimmt.
SELECT *
FROM t
WHERE RAND() < (SELECT 10 / COUNT(*) FROM t)
Erläuterung: Angenommen, Sie möchten 10 von 100 Zeilen, dann hat jede Zeile eine Wahrscheinlichkeit von 1/10, ausgewählt zu werden, was durch erreicht werden könnte WHERE RAND() < 0.1
. Dieser Ansatz garantiert nicht 10 Zeilen; Wenn die Abfrage jedoch genügend oft ausgeführt wird, beträgt die durchschnittliche Anzahl der Zeilen pro Ausführung etwa 10, und jede Zeile in der Tabelle wird gleichmäßig ausgewählt.
PREPARE stm from 'select * from table limit 10 offset ?';
SET @total = (select count(*) from table);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
Sie können auch eine where-Klausel wie diese anwenden
PREPARE stm from 'select * from table where available=true limit 10 offset ?';
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
Die Ausführung der Tabellenabfrage mit 600.000 Zeilen (700 MB) dauerte ~ 0,016 Sekunden. Festplattenlaufwerk
--EDIT--
Der Offset kann einen Wert nahe dem Ende der Tabelle annehmen, was dazu führt, dass die select-Anweisung weniger Zeilen (oder möglicherweise nur 1) zurückgibt row), um dies zu vermeiden, können wir das offset
erneut überprüfen , nachdem wir es deklariert haben
SET @rows_count = 10;
PREPARE stm from "select * from table where available=true limit ? offset ?";
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
SET @_offset = (SELECT IF(@total-@_offset<@rows_count,@_offset-@rows_count,@_offset));
SET @_offset = (SELECT IF(@_offset<0,0,@_offset));
EXECUTE stm using @rows_count,@_offset;
Ich benutze diese Abfrage:
select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10
Abfragezeit: 0,016 s
So mache ich es:
select *
from table_with_600k_rows
where rand() < 10/600000
limit 10
Ich mag es, weil keine anderen Tabellen erforderlich sind, es einfach zu schreiben ist und sehr schnell ausgeführt werden kann.
Verwenden Sie die folgende einfache Abfrage, um zufällige Daten aus einer Tabelle abzurufen.
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 10
Ich denke, das ist der bestmögliche Weg.
SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no