Ich habe dies immer in PHP gemacht, nachdem Abfrageergebnisse von SQL ... dies ist wahrscheinlich viel schneller für die Verarbeitung gemäß dem Anhang der Lösung zu Limit 1
-1 für das order by rand()Verlassen auf oder Äquivalente in allen dbs: |. auch hier erwähnt .
AD7six
20
Vor zehn Jahren sagte ein Typ, dass die Verwendung ORDER BY RAND()falsch ist ...
Trejder
ORDER BY NEWID () scheint auf SQL Server deutlich langsamer zu sein. Meine Abfrage sieht folgendermaßen aus: Wählen Sie die Top 1000 C.CustomerId, CL.LoginName aus dem inneren Join von Kunde C LinkedAccount LA auf C.CustomerId = LA.CustomerId innerer Join CustomerLogin CL auf der Gruppe C.CustomerId = CL.CustomerId von C.CustomerId, CL. LoginName mit count (*)> 1 order by NEWID () Durch Entfernen der Zeile "order by NEWID ()" werden die Ergebnisse viel schneller zurückgegeben.
Ben Power
3
Verwenden Sie für SQLite die Funktion RANDOM ().
Slam
10
Diese Lösungen skalieren nicht. Sie sind O(n)mit nder Anzahl der Datensätze in der Tabelle. Stellen Sie sich vor, Sie haben 1 Million Datensätze. Möchten Sie wirklich 1 Million Zufallszahlen oder eindeutige IDs generieren? Ich würde das lieber verwenden COUNT()und in einen neuen LIMITAusdruck mit einer einzelnen Zufallszahl einbeziehen .
Christian Hujer
174
Lösungen wie Jeremies:
SELECT*FROMtableORDERBY RAND() LIMIT 1
funktionieren, aber sie benötigen einen sequentiellen Scan der gesamten Tabelle (da der jeder Zeile zugeordnete Zufallswert berechnet werden muss, damit der kleinste ermittelt werden kann), was selbst für mittelgroße Tabellen recht langsam sein kann. Meine Empfehlung wäre, eine Art indizierte numerische Spalte zu verwenden (viele Tabellen haben diese als Primärschlüssel) und dann etwas zu schreiben wie:
SELECT*FROMtableWHERE num_value >= RAND()*(SELECT MAX (num_value )FROMtable)ORDERBY num_value LIMIT 1
Dies funktioniert in logarithmischer Zeit, unabhängig von der Tabellengröße, wenn num_valueindiziert. Eine Einschränkung: Dies setzt voraus, dass die num_valueVerteilung im Bereich gleichmäßig ist 0..MAX(num_value). Wenn Ihr Datensatz stark von dieser Annahme abweicht, erhalten Sie verzerrte Ergebnisse (einige Zeilen werden häufiger angezeigt als andere).
Der zweite Vorschlag ist nicht zufällig. Sie können nicht vorhersagen, welche Reihe ausgewählt wird, aber wenn Sie wetten müssten, würden Sie auf die zweite Reihe wetten. Und Sie würden nie auf die letzte Reihe wetten, es ist weniger wahrscheinlich, dass Sie ausgewählt werden, unabhängig von der Verteilung Ihres num_value und der Größe Ihres Tisches.
Etienne Racine
1
Ich weiß, dass RAND () -Funktionen normalerweise nicht von sehr hoher Qualität sind, aber ansonsten können Sie bitte erläutern, warum die Auswahl nicht zufällig ist.
Grey Panther
13
Der erste ist FALSCH in SQL Server. Die Funktion RAND () wird nur einmal pro Abfrage und nicht einmal pro Zeile aufgerufen. Es wird also immer die erste Zeile ausgewählt (probieren Sie es aus).
Jeff Walker Code Ranger
3
Beim zweiten wird ebenfalls davon ausgegangen, dass alle Zeilen berücksichtigt werden: Möglicherweise wird eine gelöschte Zeile ausgewählt.
Sam Rueby
3
@ Sam.Rueby Tatsächlich stellt num_value> = RAND () ... limit 1 sicher, dass leere Zeilen übersprungen werden, bis eine vorhandene Zeile gefunden wird.
Ghord
62
Ich weiß nicht, wie effizient das ist, aber ich habe es schon einmal benutzt:
SELECTTOP1*FROM MyTable ORDERBY newid()
Da GUIDs ziemlich zufällig sind, bedeutet die Reihenfolge, dass Sie eine zufällige Zeile erhalten.
Ich verwende MS SQL Server, SELECT TOP 1 * FROM some_table_name ORDER BY NEWID () hat bei mir großartig funktioniert, danke für die Ratschläge, Leute!
Das ist genau das gleiche wieORDER BY RAND() LIMIT 1
Ken Bloom
6
Dies ist auch sehr datenbankspezifisch, da es TOP 1und verwendet newid().
Grau
12
Das ist eine schlechte Idee. Diese Methode verwendet keinen Index, es sei denn, jede Spalte wird einzeln indiziert. Eine Tabelle mit 100 Millionen Datensätzen kann sehr lange dauern, bis ein Datensatz erstellt wird.
Schalten Sie den
1
@Switch und welche Lösung würden Sie vorschlagen?
Akmal Salikhov
31
ORDERBY NEWID()
nimmt 7.4 milliseconds
WHERE num_value >= RAND()*(SELECT MAX(num_value)FROMtable)
Die zweite Option wählt nicht die letzte Zeile aus. Ich weiß nicht warum - ich möchte nur darauf hinweisen.
Voldemort
7
@Voldemort: Gibt rand()eine Gleitkommazahl zurück, nwobei 0 < n < 1. Angenommen, es num_valuehandelt sich um eine Ganzzahl, wird der Rückgabewert von rand() * max(num_value)auch zu einer Ganzzahl gezwungen, wodurch alles nach dem Dezimalpunkt abgeschnitten wird. Daher rand() * max(num_value)wird immer kleiner als sein max(num_value), weshalb die letzte Zeile niemals ausgewählt wird.
Ian Kemp
Ich werde nicht effizient sein, wenn meine Daten häufig gelöscht werden. Wenn ich eine Lücke finde, muss ich die gesamte Abfrage erneut ausführen.
Loic Coenen
1
@ IanKemp Dumme Frage, warum also nicht einfach SELECT MAX (num_value) + 1 verwenden? Da rand (oder in den meisten Fällen RANDOM) [0,1] zurückgibt, erhalten Sie den gesamten Wertebereich. Ja, Sie haben Recht, Sie müssen eine Abfrage korrigieren.
TekHedd
13
Sie haben nicht angegeben, welchen Server Sie verwenden. In älteren Versionen von SQL Server können Sie Folgendes verwenden:
selecttop1*from mytable orderby newid()
In SQL Server 2005 und höher können Sie TABLESAMPLEeine zufällige Stichprobe abrufen, die wiederholbar ist:
SELECT FirstName, LastName
FROM Contact
TABLESAMPLE (1ROWS);
@ Andrew Hedges: Bestellung von NEWID () ist zu teuer
Andrei Rînea
10
Für SQL Server
newid () / order by funktioniert, ist jedoch für große Ergebnismengen sehr teuer, da für jede Zeile eine ID generiert und anschließend sortiert werden muss.
TABLESAMPLE () ist vom Standpunkt der Leistung aus gut, aber Sie erhalten eine Zusammenfassung der Ergebnisse (alle Zeilen auf einer Seite werden zurückgegeben).
Wenn Sie wirklich eine zufällige Stichprobe einzelner Zeilen wünschen, ändern Sie Ihre Abfrage so, dass Zeilen zufällig herausgefiltert werden, anstatt TABLESAMPLE zu verwenden. In der folgenden Abfrage wird beispielsweise die NEWID-Funktion verwendet, um ungefähr ein Prozent der Zeilen der Sales.SalesOrderDetail-Tabelle zurückzugeben:
Die SalesOrderID-Spalte ist im CHECKSUM-Ausdruck enthalten, sodass NEWID () einmal pro Zeile ausgewertet wird, um eine Stichprobenauswahl pro Zeile zu erzielen. Der Ausdruck CAST (CHECKSUM (NEWID (), SalesOrderID) & 0x7fffffff AS float / CAST (0x7fffffff AS int) ergibt einen zufälligen Float-Wert zwischen 0 und 1.
Wenn ich gegen eine Tabelle mit 1.000.000 Zeilen laufe, sind hier meine Ergebnisse:
SETSTATISTICS TIME ONSETSTATISTICS IO ON/* newid()
rows returned: 10000
logical reads: 3359
CPU time: 3312 ms
elapsed time = 3359 ms
*/SELECTTOP1PERCENT Number
FROM Numbers
ORDERBY newid()/* TABLESAMPLE
rows returned: 9269 (varies)
logical reads: 32
CPU time: 0 ms
elapsed time: 5 ms
*/SELECT Number
FROM Numbers
TABLESAMPLE (1PERCENT)/* Filter
rows returned: 9994 (varies)
logical reads: 3359
CPU time: 641 ms
elapsed time: 627 ms
*/SELECT Number
FROM Numbers
WHERE0.01>= CAST(CHECKSUM(NEWID(), Number)&0x7fffffffAS float)/ CAST (0x7fffffffAS int)SETSTATISTICS IO OFFSETSTATISTICS TIME OFF
Wenn Sie mit TABLESAMPLE durchkommen, erhalten Sie die beste Leistung. Verwenden Sie andernfalls die Methode newid () / filter. newid () / order by sollte der letzte Ausweg sein, wenn Sie eine große Ergebnismenge haben.
Verwenden Sie nach Möglichkeit gespeicherte Anweisungen, um die Ineffizienz beider Indizes für RND () zu vermeiden und ein Datensatznummernfeld zu erstellen.
PREPARE RandomRecord FROM "SELECT * FROM table LIMIT ?, 1";
SET @ n = FLOOR (RAND () * (SELECT COUNT (*) FROM Tabelle));
EXECUTE RandomRecord USING @n;
Diese Lösung sorgt auch dafür, dass zufällige Zeilen zurückgegeben werden, wenn der in der obigen where-Klausel verwendete indizierte numerische Wert nicht gleichmäßig verteilt ist. Selbst wenn es fast genauso lange (konstant) dauert wie bei Verwendung von id_value> = RAND () * MAX (id_value), ist es besser.
Guido
Soweit ich das beurteilen kann, läuft dies nicht in konstanter Zeit, sondern in linearer Zeit. Im schlimmsten Fall entspricht @n der Anzahl der Zeilen in der Tabelle, und "SELECT * FROM table LIMIT ?, 1" wertet @n - 1 Zeilen aus, bis die letzte erreicht ist.
Andres Riofrio
3
Der beste Weg ist, einen zufälligen Wert nur zu diesem Zweck in eine neue Spalte einzufügen und so etwas zu verwenden (Pseude-Code + SQL):
randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")
Dies ist die Lösung, die vom MediaWiki-Code verwendet wird. Natürlich gibt es eine gewisse Tendenz gegenüber kleineren Werten, aber sie stellten fest, dass es ausreichend war, den Zufallswert auf Null zu setzen, wenn keine Zeilen abgerufen wurden.
Für die Lösung newid () ist möglicherweise ein vollständiger Tabellenscan erforderlich, damit jeder Zeile eine neue Guid zugewiesen werden kann, die viel weniger leistungsfähig ist.
Die Lösung von rand () funktioniert möglicherweise überhaupt nicht (dh mit MSSQL), da die Funktion nur einmal ausgewertet wird und jeder Zeile dieselbe "Zufallszahl" zugewiesen wird.
Wenn Sie 0 Ergebnisse erhalten, erhalten Sie eine nachweislich zufällige Stichprobe (nicht nur "gut genug"). Diese Lösung lässt sich fast auf mehrzeilige Abfragen skalieren (denken Sie an "Party Shuffle"). Das Problem ist, dass die Ergebnisse in der Regel wiederholt in denselben Gruppen ausgewählt werden. Um dies zu umgehen, müssten Sie die soeben verwendeten Zufallszahlen neu verteilen. Sie könnten schummeln, indem Sie randomNo verfolgen und es aus den Ergebnissen auf max (Zufälligkeit) setzen, aber dann p (Zeile i bei Abfrage 1 UND Zeile i bei Abfrage 2) == 0, was nicht fair ist. Lassen Sie mich ein bisschen rechnen, und ich werde mich mit einem wirklich fairen Schema bei Ihnen melden.
Alsuren
3
Wenn wir für SQL Server 2005 und 2008 eine zufällige Stichprobe einzelner Zeilen (aus Books Online ) wünschen :
SELECT ID FROMTABLEWHERE ID >= My_Generated_Random ORDERBY ID LIMIT 1
Beachten Sie, dass nach Zeilen gesucht wird, deren IDs gleich oder höher als der ausgewählte Wert sind. Es ist auch möglich, nach der Zeile in der Tabelle zu suchen und eine gleiche oder niedrigere ID als My_Generated_Random zu erhalten. Ändern Sie dann die Abfrage wie folgt:
SELECT ID FROMTABLEWHERE ID <= My_Generated_Random ORDERBY ID DESC LIMIT 1
Was würde passieren, wenn die generierte Zufalls-ID nicht mehr in der Tabelle vorhanden ist? Gelöschte oder passive Zeilen, die Sie dem Benutzer nicht anzeigen möchten, verursachen Probleme.
Ebleme
Nichts. Sie erhalten die NÄCHSTE, nicht genaue ID-Nummer. Wenn Sie der Meinung sind, dass id = 1 entfernt werden soll, tauschen Sie 1 mit Minimum aus.
Forsberg
2
Wie in @ BillKarwins Kommentar zu @ cnus Antwort ausgeführt ...
Beim Kombinieren mit einem LIMIT habe ich festgestellt, dass es (zumindest mit PostgreSQL 9.1) viel besser funktioniert, sich einer zufälligen Reihenfolge anzuschließen, als die tatsächlichen Zeilen direkt zu ordnen: z
SELECT*FROM tbl_post AS t
JOIN...JOIN(SELECT id, CAST(-2147483648* RANDOM()AS integer)AS rand
FROM tbl_post
WHERE create_time >=1349928000) r ON r.id = t.id
WHERE create_time >=1349928000AND...ORDERBY r.rand
LIMIT 100
Stellen Sie einfach sicher, dass das 'r' für jeden möglichen Schlüsselwert in der komplexen Abfrage, die damit verbunden ist, einen 'rand'-Wert generiert, aber beschränken Sie die Anzahl der Zeilen von' r 'nach Möglichkeit.
Das CAST als Ganzzahl ist besonders hilfreich für PostgreSQL 9.2, das eine spezifische Sortieroptimierung für Floating-Typen mit Ganzzahl und einfacher Genauigkeit bietet.
Die meisten Lösungen hier zielen darauf ab, das Sortieren zu vermeiden, müssen jedoch noch einen sequentiellen Scan über eine Tabelle durchführen.
Es gibt auch eine Möglichkeit, den sequentiellen Scan zu vermeiden, indem Sie zum Index-Scan wechseln. Wenn Sie den Indexwert Ihrer zufälligen Zeile kennen, können Sie das Ergebnis fast augenblicklich erhalten. Das Problem ist - wie man einen Indexwert errät.
Die folgende Lösung funktioniert unter PostgreSQL 8.4:
explain analyze select*from cms_refs where rec_id in(select(random()*(select last_value from cms_refs_rec_id_seq))::bigint
from generate_series(1,10))
limit 1;
In der obigen Lösung erraten Sie 10 verschiedene zufällige Indexwerte aus dem Bereich 0 .. [letzter Wert von id].
Die Zahl 10 ist willkürlich - Sie können 100 oder 1000 verwenden, da dies (erstaunlicherweise) keinen großen Einfluss auf die Reaktionszeit hat.
Es gibt auch ein Problem: Wenn Sie spärliche IDs haben, werden Sie diese möglicherweise übersehen . Die Lösung besteht darin , einen Sicherungsplan zu haben :) In diesem Fall eine reine alte Bestellung per zufälliger () Abfrage. Wenn die kombinierte ID so aussieht:
explain analyze select*from cms_refs where rec_id in(select(random()*(select last_value from cms_refs_rec_id_seq))::bigint
from generate_series(1,10))unionall(select*from cms_refs orderby random() limit 1)
limit 1;
Nicht die Union ALL- Klausel. In diesem Fall wird der zweite Teil NIEMALS ausgeführt, wenn der erste Teil Daten zurückgibt!
Spät, aber über Google hierher gekommen, werde ich der Nachwelt halber eine alternative Lösung hinzufügen.
Ein anderer Ansatz besteht darin, TOP zweimal mit abwechselnden Bestellungen zu verwenden. Ich weiß nicht, ob es sich um "reines SQL" handelt, da es eine Variable im TOP verwendet, aber es funktioniert in SQL Server 2008. Hier ist ein Beispiel, das ich für eine Tabelle mit Wörterbuchwörtern verwende, wenn ich ein zufälliges Wort möchte.
SELECTTOP1
word
FROM(SELECTTOP(@idx)
word
FROM
dbo.DictionaryAbridged WITH(NOLOCK)ORDERBY
word DESC)AS D
ORDERBY
word ASC
Natürlich ist @idx eine zufällig generierte Ganzzahl, die in der Zieltabelle einschließlich von 1 bis COUNT (*) reicht. Wenn Ihre Spalte indiziert ist, profitieren Sie auch davon. Ein weiterer Vorteil ist, dass Sie es in einer Funktion verwenden können, da NEWID () nicht zulässig ist.
Schließlich wird die obige Abfrage in etwa 1/10 der Ausführungszeit einer NEWID () - Abfrage in derselben Tabelle ausgeführt. YYMV.
Nachdem ich viele der Antworten getestet habe, glaube ich, dass dies die beste ist. Es scheint schnell zu sein und wählt jedes Mal eine gute Zufallszahl. Es scheint dem zweiten Vorschlag von @GreyPanther oben ähnlich zu sein, aber diese Antwort wählt mehr Zufallszahlen aus.
Jeff Baker
1
Ich habe diese Variation in den Antworten noch nicht ganz gesehen. Ich hatte eine zusätzliche Einschränkung, bei der ich bei einem anfänglichen Startwert jedes Mal denselben Satz von Zeilen auswählen musste.
NewId()ist unwesentlich langsamer als rand(checksum(*)), daher möchten Sie es möglicherweise nicht für große Datensatzgruppen verwenden.
Auswahl mit Initial Seed:
declare@seed int
set@seed = Year(getdate())* month(getdate())/* any other initial seed here */selecttop10percent*from table_name
orderby rand(checksum(*)% seed)/* any other math function here */
Wenn Sie denselben Satz für einen Startwert auswählen müssen, scheint dies zu funktionieren.
In SQL Server können Sie TABLESAMPLE mit NEWID () kombinieren, um eine ziemlich gute Zufälligkeit zu erzielen und trotzdem Geschwindigkeit zu haben. Dies ist besonders nützlich, wenn Sie wirklich nur 1 oder eine kleine Anzahl von Zeilen möchten.
select*from MyTable ORDERBY id OFFSET n ROWFETCH NEXT 1ROWS ONLY
Dabei ist id eine Identitätsspalte und n die gewünschte Zeile - berechnet als Zufallszahl zwischen 0 und count () - 1 der Tabelle (Offset 0 ist schließlich die erste Zeile).
Dies funktioniert mit Löchern in den Tabellendaten, solange Sie einen Index für die ORDER BY-Klausel haben. Es ist auch sehr gut für die Zufälligkeit - wenn Sie das selbst herausarbeiten, um es weiterzugeben, aber die Probleme bei anderen Methoden sind nicht vorhanden. Außerdem ist die Leistung ziemlich gut, bei einem kleineren Datensatz hält sie gut, obwohl ich keine ernsthaften Leistungstests für mehrere Millionen Zeilen ausprobiert habe.
Vor zehn Jahren (2005) sagte ein Typ , dass die Verwendung ORDER BY RAND()falsch ist ...
Trejder
0
Ich muss CD-MaN zustimmen: Die Verwendung von "ORDER BY RAND ()" funktioniert gut für kleine Tabellen oder wenn Sie SELECT nur einige Male ausführen.
Ich verwende auch die Technik "num_value> = RAND () * ...", und wenn ich wirklich zufällige Ergebnisse erzielen möchte, habe ich eine spezielle "zufällige" Spalte in der Tabelle, die ich etwa einmal am Tag aktualisiere. Dieser einzelne UPDATE-Lauf dauert einige Zeit (insbesondere, weil Sie einen Index für diese Spalte benötigen), ist jedoch viel schneller als das Erstellen von Zufallszahlen für jede Zeile bei jedem Ausführen der Auswahl.
Seien Sie vorsichtig, da TableSample keine zufällige Stichprobe von Zeilen zurückgibt. Es leitet Ihre Abfrage an, eine zufällige Stichprobe der 8-KB-Seiten zu betrachten, aus denen Ihre Zeile besteht. Anschließend wird Ihre Abfrage anhand der auf diesen Seiten enthaltenen Daten ausgeführt. Aufgrund der Gruppierung von Daten auf diesen Seiten (Einfügereihenfolge usw.) kann dies zu Daten führen, die eigentlich keine Zufallsstichprobe sind.
Es scheint, dass viele der aufgelisteten Ideen immer noch die Reihenfolge verwenden
Wenn Sie jedoch eine temporäre Tabelle verwenden, können Sie einen zufälligen Index zuweisen (wie viele der Lösungen vorgeschlagen haben) und dann den ersten Index abrufen, der größer als eine beliebige Zahl zwischen 0 und 1 ist.
Zum Beispiel (für DB2):
WITH TEMP AS(SELECT COMLUMN, RAND()AS IDX FROMTABLE)SELECTCOLUMNFROMTABLEWHERE IDX >.5FETCH FIRST 1ROW ONLY
Nachdem ich über diese Lösung nachgedacht habe, habe ich einen grundlegenden Fehler in meiner Logik gefunden. Dies würde konsistent dieselben kleinen Einstellungswerte am Anfang der Tabelle zurückgeben, da ich davon ausgehe, dass bei einer gleichmäßigen Verteilung zwischen 0 und 1 eine 50% ige Wahrscheinlichkeit besteht, dass die erste Zeile diese Kriterien erfüllt.
Es gibt eine bessere Lösung für Oracle, anstatt dbms_random.value zu verwenden, während ein vollständiger Scan erforderlich ist, um Zeilen nach dbms_random.value zu ordnen, und es ist für große Tabellen ziemlich langsam.
Erweitern Sie für SQL Server 2005 und höher die Antwort von @ GreyPanther für Fälle, in denen num_valuekeine kontinuierlichen Werte vorhanden sind. Dies funktioniert auch in Fällen, in denen wir Datensätze nicht gleichmäßig verteilt haben und in denen num_valuees sich nicht um eine Zahl, sondern um eine eindeutige Kennung handelt.
WITH CTE_Table (SelRow, num_value)AS(SELECT ROW_NUMBER()OVER(ORDERBY ID)AS SelRow, num_value FROMtable)SELECT*FROMtableWhere num_value =(SELECTTOP1 num_value FROM CTE_Table WHERE SelRow >= RAND()*(SELECT MAX(SelRow)FROM CTE_Table))
Antworten:
Siehe diesen Beitrag: SQL zum Auswählen einer zufälligen Zeile aus einer Datenbanktabelle . Hierzu werden Methoden in MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 und Oracle beschrieben (Folgendes wird von diesem Link kopiert):
Wählen Sie mit MySQL eine zufällige Zeile aus:
Wählen Sie mit PostgreSQL eine zufällige Zeile aus:
Wählen Sie mit Microsoft SQL Server eine zufällige Zeile aus:
Wählen Sie mit IBM DB2 eine zufällige Zeile aus
Wählen Sie mit Oracle einen zufälligen Datensatz aus:
quelle
order by rand()
Verlassen auf oder Äquivalente in allen dbs: |. auch hier erwähnt .ORDER BY RAND()
falsch ist ...O(n)
mitn
der Anzahl der Datensätze in der Tabelle. Stellen Sie sich vor, Sie haben 1 Million Datensätze. Möchten Sie wirklich 1 Million Zufallszahlen oder eindeutige IDs generieren? Ich würde das lieber verwendenCOUNT()
und in einen neuenLIMIT
Ausdruck mit einer einzelnen Zufallszahl einbeziehen .Lösungen wie Jeremies:
funktionieren, aber sie benötigen einen sequentiellen Scan der gesamten Tabelle (da der jeder Zeile zugeordnete Zufallswert berechnet werden muss, damit der kleinste ermittelt werden kann), was selbst für mittelgroße Tabellen recht langsam sein kann. Meine Empfehlung wäre, eine Art indizierte numerische Spalte zu verwenden (viele Tabellen haben diese als Primärschlüssel) und dann etwas zu schreiben wie:
Dies funktioniert in logarithmischer Zeit, unabhängig von der Tabellengröße, wenn
num_value
indiziert. Eine Einschränkung: Dies setzt voraus, dass dienum_value
Verteilung im Bereich gleichmäßig ist0..MAX(num_value)
. Wenn Ihr Datensatz stark von dieser Annahme abweicht, erhalten Sie verzerrte Ergebnisse (einige Zeilen werden häufiger angezeigt als andere).quelle
Ich weiß nicht, wie effizient das ist, aber ich habe es schon einmal benutzt:
Da GUIDs ziemlich zufällig sind, bedeutet die Reihenfolge, dass Sie eine zufällige Zeile erhalten.
quelle
ORDER BY RAND() LIMIT 1
TOP 1
und verwendetnewid()
.nimmt
7.4 milliseconds
nimmt
0.0065 milliseconds
!Ich werde definitiv mit letzterer Methode gehen.
quelle
rand()
eine Gleitkommazahl zurück,n
wobei0 < n < 1
. Angenommen, esnum_value
handelt sich um eine Ganzzahl, wird der Rückgabewert vonrand() * max(num_value)
auch zu einer Ganzzahl gezwungen, wodurch alles nach dem Dezimalpunkt abgeschnitten wird. Daherrand() * max(num_value)
wird immer kleiner als seinmax(num_value)
, weshalb die letzte Zeile niemals ausgewählt wird.Sie haben nicht angegeben, welchen Server Sie verwenden. In älteren Versionen von SQL Server können Sie Folgendes verwenden:
In SQL Server 2005 und höher können Sie
TABLESAMPLE
eine zufällige Stichprobe abrufen, die wiederholbar ist:quelle
Für SQL Server
newid () / order by funktioniert, ist jedoch für große Ergebnismengen sehr teuer, da für jede Zeile eine ID generiert und anschließend sortiert werden muss.
TABLESAMPLE () ist vom Standpunkt der Leistung aus gut, aber Sie erhalten eine Zusammenfassung der Ergebnisse (alle Zeilen auf einer Seite werden zurückgegeben).
Für eine bessere echte Zufallsstichprobe ist es am besten, Zeilen zufällig herauszufiltern. Ich habe das folgende Codebeispiel im SQL Server Books Online-Artikel Einschränken von Ergebnismengen mithilfe von TABLESAMPLE gefunden :
Wenn ich gegen eine Tabelle mit 1.000.000 Zeilen laufe, sind hier meine Ergebnisse:
Wenn Sie mit TABLESAMPLE durchkommen, erhalten Sie die beste Leistung. Verwenden Sie andernfalls die Methode newid () / filter. newid () / order by sollte der letzte Ausweg sein, wenn Sie eine große Ergebnismenge haben.
quelle
Verwenden Sie nach Möglichkeit gespeicherte Anweisungen, um die Ineffizienz beider Indizes für RND () zu vermeiden und ein Datensatznummernfeld zu erstellen.
quelle
Der beste Weg ist, einen zufälligen Wert nur zu diesem Zweck in eine neue Spalte einzufügen und so etwas zu verwenden (Pseude-Code + SQL):
Dies ist die Lösung, die vom MediaWiki-Code verwendet wird. Natürlich gibt es eine gewisse Tendenz gegenüber kleineren Werten, aber sie stellten fest, dass es ausreichend war, den Zufallswert auf Null zu setzen, wenn keine Zeilen abgerufen wurden.
Für die Lösung newid () ist möglicherweise ein vollständiger Tabellenscan erforderlich, damit jeder Zeile eine neue Guid zugewiesen werden kann, die viel weniger leistungsfähig ist.
Die Lösung von rand () funktioniert möglicherweise überhaupt nicht (dh mit MSSQL), da die Funktion nur einmal ausgewertet wird und jeder Zeile dieselbe "Zufallszahl" zugewiesen wird.
quelle
Wenn wir für SQL Server 2005 und 2008 eine zufällige Stichprobe einzelner Zeilen (aus Books Online ) wünschen :
quelle
Wenn Sie RAND () verwenden, da dies nicht empfohlen wird , erhalten Sie möglicherweise einfach die maximale ID (= Max):
Holen Sie sich einen Zufall zwischen 1..Max (= My_Generated_Random)
und führen Sie dann diese SQL aus:
Beachten Sie, dass nach Zeilen gesucht wird, deren IDs gleich oder höher als der ausgewählte Wert sind. Es ist auch möglich, nach der Zeile in der Tabelle zu suchen und eine gleiche oder niedrigere ID als My_Generated_Random zu erhalten. Ändern Sie dann die Abfrage wie folgt:
quelle
Wie in @ BillKarwins Kommentar zu @ cnus Antwort ausgeführt ...
Beim Kombinieren mit einem LIMIT habe ich festgestellt, dass es (zumindest mit PostgreSQL 9.1) viel besser funktioniert, sich einer zufälligen Reihenfolge anzuschließen, als die tatsächlichen Zeilen direkt zu ordnen: z
Stellen Sie einfach sicher, dass das 'r' für jeden möglichen Schlüsselwert in der komplexen Abfrage, die damit verbunden ist, einen 'rand'-Wert generiert, aber beschränken Sie die Anzahl der Zeilen von' r 'nach Möglichkeit.
Das CAST als Ganzzahl ist besonders hilfreich für PostgreSQL 9.2, das eine spezifische Sortieroptimierung für Floating-Typen mit Ganzzahl und einfacher Genauigkeit bietet.
quelle
Die meisten Lösungen hier zielen darauf ab, das Sortieren zu vermeiden, müssen jedoch noch einen sequentiellen Scan über eine Tabelle durchführen.
Es gibt auch eine Möglichkeit, den sequentiellen Scan zu vermeiden, indem Sie zum Index-Scan wechseln. Wenn Sie den Indexwert Ihrer zufälligen Zeile kennen, können Sie das Ergebnis fast augenblicklich erhalten. Das Problem ist - wie man einen Indexwert errät.
Die folgende Lösung funktioniert unter PostgreSQL 8.4:
In der obigen Lösung erraten Sie 10 verschiedene zufällige Indexwerte aus dem Bereich 0 .. [letzter Wert von id].
Die Zahl 10 ist willkürlich - Sie können 100 oder 1000 verwenden, da dies (erstaunlicherweise) keinen großen Einfluss auf die Reaktionszeit hat.
Es gibt auch ein Problem: Wenn Sie spärliche IDs haben, werden Sie diese möglicherweise übersehen . Die Lösung besteht darin , einen Sicherungsplan zu haben :) In diesem Fall eine reine alte Bestellung per zufälliger () Abfrage. Wenn die kombinierte ID so aussieht:
Nicht die Union ALL- Klausel. In diesem Fall wird der zweite Teil NIEMALS ausgeführt, wenn der erste Teil Daten zurückgibt!
quelle
Spät, aber über Google hierher gekommen, werde ich der Nachwelt halber eine alternative Lösung hinzufügen.
Ein anderer Ansatz besteht darin, TOP zweimal mit abwechselnden Bestellungen zu verwenden. Ich weiß nicht, ob es sich um "reines SQL" handelt, da es eine Variable im TOP verwendet, aber es funktioniert in SQL Server 2008. Hier ist ein Beispiel, das ich für eine Tabelle mit Wörterbuchwörtern verwende, wenn ich ein zufälliges Wort möchte.
Natürlich ist @idx eine zufällig generierte Ganzzahl, die in der Zieltabelle einschließlich von 1 bis COUNT (*) reicht. Wenn Ihre Spalte indiziert ist, profitieren Sie auch davon. Ein weiterer Vorteil ist, dass Sie es in einer Funktion verwenden können, da NEWID () nicht zulässig ist.
Schließlich wird die obige Abfrage in etwa 1/10 der Ausführungszeit einer NEWID () - Abfrage in derselben Tabelle ausgeführt. YYMV.
quelle
Sie können auch versuchen, die
new id()
Funktion zu verwenden.Schreiben Sie einfach Ihre Anfrage und verwenden Sie die Reihenfolge nach
new id()
Funktion. Es ist ziemlich zufällig.quelle
Damit MySQL zufällige Aufzeichnungen erhält
Weitere Details http://jan.kneschke.de/projects/mysql/order-by-rand/
quelle
Ich habe diese Variation in den Antworten noch nicht ganz gesehen. Ich hatte eine zusätzliche Einschränkung, bei der ich bei einem anfänglichen Startwert jedes Mal denselben Satz von Zeilen auswählen musste.
Für MS SQL:
Minimales Beispiel:
Normalisierte Ausführungszeit: 1,00
NewId () Beispiel:
Normalisierte Ausführungszeit: 1.02
NewId()
ist unwesentlich langsamer alsrand(checksum(*))
, daher möchten Sie es möglicherweise nicht für große Datensatzgruppen verwenden.Auswahl mit Initial Seed:
Wenn Sie denselben Satz für einen Startwert auswählen müssen, scheint dies zu funktionieren.
quelle
In MSSQL (getestet am 11.0.5569) mit
ist deutlich schneller als
quelle
In SQL Server können Sie TABLESAMPLE mit NEWID () kombinieren, um eine ziemlich gute Zufälligkeit zu erzielen und trotzdem Geschwindigkeit zu haben. Dies ist besonders nützlich, wenn Sie wirklich nur 1 oder eine kleine Anzahl von Zeilen möchten.
quelle
Mit SQL Server 2012+ können Sie die OFFSET FETCH-Abfrage verwenden , um dies für eine einzelne zufällige Zeile zu tun
Dabei ist id eine Identitätsspalte und n die gewünschte Zeile - berechnet als Zufallszahl zwischen 0 und count () - 1 der Tabelle (Offset 0 ist schließlich die erste Zeile).
Dies funktioniert mit Löchern in den Tabellendaten, solange Sie einen Index für die ORDER BY-Klausel haben. Es ist auch sehr gut für die Zufälligkeit - wenn Sie das selbst herausarbeiten, um es weiterzugeben, aber die Probleme bei anderen Methoden sind nicht vorhanden. Außerdem ist die Leistung ziemlich gut, bei einem kleineren Datensatz hält sie gut, obwohl ich keine ernsthaften Leistungstests für mehrere Millionen Zeilen ausprobiert habe.
quelle
quelle
ORDER BY RAND()
falsch ist ...Ich muss CD-MaN zustimmen: Die Verwendung von "ORDER BY RAND ()" funktioniert gut für kleine Tabellen oder wenn Sie SELECT nur einige Male ausführen.
Ich verwende auch die Technik "num_value> = RAND () * ...", und wenn ich wirklich zufällige Ergebnisse erzielen möchte, habe ich eine spezielle "zufällige" Spalte in der Tabelle, die ich etwa einmal am Tag aktualisiere. Dieser einzelne UPDATE-Lauf dauert einige Zeit (insbesondere, weil Sie einen Index für diese Spalte benötigen), ist jedoch viel schneller als das Erstellen von Zufallszahlen für jede Zeile bei jedem Ausführen der Auswahl.
quelle
Seien Sie vorsichtig, da TableSample keine zufällige Stichprobe von Zeilen zurückgibt. Es leitet Ihre Abfrage an, eine zufällige Stichprobe der 8-KB-Seiten zu betrachten, aus denen Ihre Zeile besteht. Anschließend wird Ihre Abfrage anhand der auf diesen Seiten enthaltenen Daten ausgeführt. Aufgrund der Gruppierung von Daten auf diesen Seiten (Einfügereihenfolge usw.) kann dies zu Daten führen, die eigentlich keine Zufallsstichprobe sind.
Siehe: http://www.mssqltips.com/tip.asp?tip=1308
Diese MSDN-Seite für TableSample enthält ein Beispiel für die Generierung einer tatsächlich zufälligen Stichprobe von Daten.
http://msdn.microsoft.com/en-us/library/ms189108.aspx
quelle
Es scheint, dass viele der aufgelisteten Ideen immer noch die Reihenfolge verwenden
Wenn Sie jedoch eine temporäre Tabelle verwenden, können Sie einen zufälligen Index zuweisen (wie viele der Lösungen vorgeschlagen haben) und dann den ersten Index abrufen, der größer als eine beliebige Zahl zwischen 0 und 1 ist.
Zum Beispiel (für DB2):
quelle
Ein einfacher und effizienter Weg von http://akinas.com/pages/en/blog/mysql_random_row/
quelle
Es gibt eine bessere Lösung für Oracle, anstatt dbms_random.value zu verwenden, während ein vollständiger Scan erforderlich ist, um Zeilen nach dbms_random.value zu ordnen, und es ist für große Tabellen ziemlich langsam.
Verwenden Sie stattdessen Folgendes:
quelle
Für Firebird:
quelle
Erweitern Sie für SQL Server 2005 und höher die Antwort von @ GreyPanther für Fälle, in denen
num_value
keine kontinuierlichen Werte vorhanden sind. Dies funktioniert auch in Fällen, in denen wir Datensätze nicht gleichmäßig verteilt haben und in denennum_value
es sich nicht um eine Zahl, sondern um eine eindeutige Kennung handelt.quelle
Zufällige Funktionen aus dem SQL könnten helfen. Auch wenn Sie sich auf nur eine Zeile beschränken möchten, fügen Sie diese am Ende hinzu.
quelle