Frage

Wie kann ich in reinem SQL eine zufällige Zeile (oder eine möglichst zufällige Zeile) anfordern?

War es hilfreich?

Lösung

Siehe diesen Beitrag: SQL zum Auswählen einer zufälligen Zeile aus einer Datenbanktabelle.Es werden Methoden dafür in MySQL, PostgreSQL, Microsoft SQL Server, IBM DB2 und Oracle beschrieben (das Folgende wurde von diesem Link kopiert):

Wählen Sie mit MySQL eine zufällige Zeile aus:

SELECT column FROM table
ORDER BY RAND()
LIMIT 1

Wählen Sie mit PostgreSQL eine zufällige Zeile aus:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

Wählen Sie eine zufällige Zeile mit Microsoft SQL Server aus:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

Wählen Sie mit IBM DB2 eine zufällige Zeile aus

SELECT column, RAND() as IDX 
FROM table 
ORDER BY IDX FETCH FIRST 1 ROWS ONLY

Wählen Sie einen zufälligen Datensatz mit Oracle aus:

SELECT column FROM
( SELECT column FROM table
ORDER BY dbms_random.value )
WHERE rownum = 1

Andere Tipps

Lösungen wie Jeremys:

SELECT * FROM table ORDER BY RAND() LIMIT 1

funktionieren, aber sie erfordern einen sequentiellen Scan der gesamten Tabelle (da der mit jeder Zeile verknüpfte Zufallswert berechnet werden muss, damit der kleinste ermittelt werden kann), was selbst bei mittelgroßen 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 * FROM table WHERE num_value >= RAND() * 
    ( SELECT MAX (num_value ) FROM table ) 
ORDER BY num_value LIMIT 1

Dies funktioniert in logarithmischer Zeit, unabhängig von der Tabellengröße, wenn num_value ist indiziert.Eine Einschränkung:das setzt das voraus num_value ist im Verbreitungsgebiet gleichmäßig verteilt 0..MAX(num_value).Wenn Ihr Datensatz stark von dieser Annahme abweicht, erhalten Sie verzerrte Ergebnisse (einige Zeilen werden häufiger angezeigt als andere).

Ich weiß nicht, wie effizient das ist, aber ich habe es schon einmal verwendet:

SELECT TOP 1 * FROM MyTable ORDER BY newid()

Da GUIDs ziemlich zufällig sind, bedeutet die Reihenfolge, dass Sie eine zufällige Zeile erhalten.

ORDER BY NEWID()

dauert 7.4 milliseconds

WHERE num_value >= RAND() * (SELECT MAX(num_value) FROM table)

dauert 0.0065 milliseconds!

Ich werde auf jeden Fall die letztere Methode wählen.

Sie haben nicht gesagt, welchen Server Sie verwenden.In älteren Versionen von SQL Server können Sie Folgendes verwenden:

select top 1 * from mytable order by newid()

In SQL Server 2005 und höher können Sie verwenden TABLESAMPLE So erhalten Sie eine wiederholbare Zufallsstichprobe:

SELECT FirstName, LastName
FROM Contact 
TABLESAMPLE (1 ROWS) ;

Für SQL Server

newid()/order by wird funktionieren, ist aber bei großen Ergebnismengen sehr teuer, da für jede Zeile eine ID generiert und diese dann sortiert werden muss.

TABLESAMPLE() ist aus Leistungssicht gut, aber es kommt zu einer Verklumpung der Ergebnisse (alle Zeilen auf einer Seite werden zurückgegeben).

Um eine echte Zufallsstichprobe mit höherer Leistung zu erhalten, besteht die beste Möglichkeit darin, Zeilen nach dem Zufallsprinzip herauszufiltern.Ich habe das folgende Codebeispiel im Artikel „SQL Server Books Online“ gefunden Einschränken der Ergebnismengen durch die Verwendung von TABLESAMPLE:

Wenn Sie wirklich eine zufällige Stichprobe einzelner Zeilen wünschen, ändern Sie Ihre Abfrage, um die Zeilen zufällig herauszufiltern, anstatt das Beispiel zu verwenden.Beispielsweise verwendet die folgende Abfrage die Newid -Funktion, um ungefähr ein Prozent der Zeilen des Umsatzes zurückzugeben. SalesOrderDetail -Tabelle:

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(),SalesOrderID) & 0x7fffffff AS float)
              / CAST (0x7fffffff AS int)

Die Spalte von SalesOrderID ist im Prüfsummenausdruck enthalten, so dass Newid () einmal pro Zeile bewertet, um die Probenahme pro Reisebasis zu erreichen.Der Ausdruckskaste (Checksumme (Newid (), SalesOrderID) & 0x7fffffff als Float / Cast (0x7FFFFFFF als int) bewertet einen zufälligen Float -Wert zwischen 0 und 1.

Bei der Ausführung mit einer Tabelle mit 1.000.000 Zeilen sind hier meine Ergebnisse:

SET STATISTICS TIME ON
SET STATISTICS IO ON

/* newid()
   rows returned: 10000
   logical reads: 3359
   CPU time: 3312 ms
   elapsed time = 3359 ms
*/
SELECT TOP 1 PERCENT Number
FROM Numbers
ORDER BY newid()

/* TABLESAMPLE
   rows returned: 9269 (varies)
   logical reads: 32
   CPU time: 0 ms
   elapsed time: 5 ms
*/
SELECT Number
FROM Numbers
TABLESAMPLE (1 PERCENT)

/* Filter
   rows returned: 9994 (varies)
   logical reads: 3359
   CPU time: 641 ms
   elapsed time: 627 ms
*/    
SELECT Number
FROM Numbers
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), Number) & 0x7fffffff AS float) 
              / CAST (0x7fffffff AS int)

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

Wenn Sie auf die Verwendung von TABLESAMPLE verzichten können, erhalten Sie die beste Leistung.Andernfalls verwenden Sie 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() und die Erstellung eines Datensatznummernfelds zu vermeiden.

PREPARE RandomRecord FROM "SELECT * FROM table LIMIT ?,1";
SET @n=FLOOR(RAND()*(SELECT COUNT(*) FROM table));
EXECUTE RandomRecord USING @n;

Der beste Weg besteht darin, zu diesem Zweck einen Zufallswert in eine neue Spalte einzufügen und etwa Folgendes zu verwenden (Pseudcode + SQL):

randomNo = random()
execSql("SELECT TOP 1 * FROM MyTable WHERE MyTable.Randomness > $randomNo")

Dies ist die vom MediaWiki-Code verwendete Lösung.Natürlich gibt es eine gewisse Voreingenommenheit gegenüber kleineren Werten, aber sie fanden heraus, dass es ausreichte, den Zufallswert auf Null zu setzen, wenn keine Zeilen abgerufen werden.

Die Lösung newid() erfordert möglicherweise einen vollständigen Tabellenscan, damit jeder Zeile eine neue GUID zugewiesen werden kann, was wesentlich weniger leistungsfähig ist.

Die rand()-Lösung funktioniert möglicherweise überhaupt nicht (d. h.mit MSSQL), da die Funktion nur einmal ausgewertet wird, und jeden Zeile wird die gleiche „zufällige“ Nummer zugewiesen.

Wenn wir für SQL Server 2005 und 2008 eine Zufallsstichprobe einzelner Zeilen wünschen (von Bücher online):

SELECT * FROM Sales.SalesOrderDetail
WHERE 0.01 >= CAST(CHECKSUM(NEWID(), SalesOrderID) & 0x7fffffff AS float)
/ CAST (0x7fffffff AS int)

Anstelle von Verwenden von RAND(), da dies nicht empfohlen wird, können Sie einfach die maximale ID (=Max) erhalten:

SELECT MAX(ID) FROM TABLE;

Erhalte eine Zufallszahl zwischen 1..Max (=My_Generated_Random)

My_Generated_Random = rand_in_your_programming_lang_function(1..Max);

und führen Sie dann dieses SQL aus:

SELECT ID FROM TABLE WHERE ID >= My_Generated_Random ORDER BY ID LIMIT 1

Beachten Sie, dass nach allen Zeilen gesucht wird, deren IDs GLEICH oder HÖHER als der ausgewählte Wert sind.Es ist auch möglich, nach der Zeile unten in der Tabelle zu suchen und eine gleiche oder niedrigere ID als My_Generated_Random zu erhalten und dann die Abfrage wie folgt zu ändern:

SELECT ID FROM TABLE WHERE ID <= My_Generated_Random ORDER BY ID DESC LIMIT 1

Wie im Kommentar von @BillKarwin zur Antwort von @cnu erwähnt ...

Bei der Kombination mit einem LIMIT habe ich festgestellt, dass es (zumindest mit PostgreSQL 9.1) viel besser funktioniert, JOIN mit einer zufälligen Reihenfolge zu verwenden, als die tatsächlichen Zeilen direkt zu ordnen:z.B.

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 >= 1349928000 AND ...
ORDER BY r.rand
LIMIT 100

Stellen Sie einfach sicher, dass „r“ einen „Rand“-Wert für jeden möglichen Schlüsselwert in der damit verknüpften komplexen Abfrage generiert, begrenzen Sie jedoch die Anzahl der Zeilen von „r“ nach Möglichkeit.

CAST als Ganzzahl ist besonders hilfreich für PostgreSQL 9.2, das über eine spezielle Sortieroptimierung für Ganzzahl- und Gleitkommatypen mit einfacher Genauigkeit verfügt.

Die meisten Lösungen hier zielen darauf ab, das Sortieren zu vermeiden, müssen aber trotzdem 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 Zufallszeile kennen, können Sie das Ergebnis fast sofort erhalten.Das Problem besteht darin, 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 der ID].

Die Zahl 10 ist willkürlich – Sie können 100 oder 1000 verwenden, da sie (erstaunlicherweise) keinen großen Einfluss auf die Reaktionszeit hat.

Es gibt auch ein Problem – wenn Sie spärliche IDs haben Du könntest es verpassen.Die Lösung ist einen Backup-Plan haben :) In diesem Fall eine reine Old-Order-by-random()-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))
    union all (select * from cms_refs order by random() limit 1)
    limit 1;

Nicht der Union ALLE Klausel.In diesem Fall wird der zweite Teil NIEMALS ausgeführt, wenn der erste Teil Daten zurückgibt!

Zu spät, aber über Google hierher gekommen, daher werde ich der Nachwelt zuliebe eine alternative Lösung hinzufügen.

Ein anderer Ansatz besteht darin, TOP zweimal mit abwechselnder Reihenfolge zu verwenden.Ich weiß nicht, ob es „reines SQL“ ist, da es eine Variable im TOP verwendet, aber es funktioniert in SQL Server 2008.Hier ist ein Beispiel, das ich gegen eine Tabelle mit Wörterbuchwörtern verwende, wenn ich ein zufälliges Wort möchte.

SELECT TOP 1
  word
FROM (
  SELECT TOP(@idx)
    word 
  FROM
    dbo.DictionaryAbridged WITH(NOLOCK)
  ORDER BY
    word DESC
) AS D
ORDER BY
  word ASC

Natürlich ist @idx eine zufällig generierte Ganzzahl, die von 1 bis einschließlich COUNT(*) in der Zieltabelle reicht.Wenn Ihre Kolumne indiziert ist, profitieren auch Sie davon.Ein weiterer Vorteil besteht darin, 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 Abfrage vom Typ NEWID() für dieselbe Tabelle ausgeführt.YYMV.

Sie können es auch versuchen new id() Funktion.

Schreiben Sie einfach Ihre Anfrage und verwenden Sie „Bestellen nach“. new id() Funktion.Es ist ziemlich zufällig.

Damit MySQL einen zufälligen Datensatz erhält

 SELECT name
  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

Mehr Details http://jan.kneschke.de/projects/mysql/order-by-rand/

Ich habe diese Variation in den Antworten noch nicht ganz gesehen.Ich hatte eine zusätzliche Einschränkung, bei der ich bei einem gegebenen Anfangsstartwert jedes Mal denselben Zeilensatz auswählen musste.

Für MS SQL:

Minimalbeispiel:

select top 10 percent *
from table_name
order by rand(checksum(*))

Normalisierte Ausführungszeit:1,00

NewId()-Beispiel:

select top 10 percent *
from table_name
order by newid()

Normalisierte Ausführungszeit:1.02

NewId() ist unwesentlich langsamer als rand(checksum(*)), Daher möchten Sie es möglicherweise nicht für große Datensatzmengen verwenden.

Selektion mit Erstsaat:

declare @seed int
set @seed = Year(getdate()) * month(getdate()) /* any other initial seed here */

select top 10 percent *
from table_name
order by rand(checksum(*) % seed) /* any other math function here */

Wenn Sie bei gegebenem Startwert denselben Satz auswählen müssen, scheint dies zu funktionieren.

In MSSQL (getestet am 11.0.5569) mit

SELECT TOP 100 * FROM employee ORDER BY CRYPT_GEN_RANDOM(10)

ist deutlich schneller als

SELECT TOP 100 * FROM employee ORDER BY NEWID()

In SQL Server können Sie TABLESAMPLE mit NEWID() kombinieren, um eine ziemlich gute Zufälligkeit zu erreichen und trotzdem Geschwindigkeit zu haben.Dies ist besonders nützlich, wenn Sie wirklich nur eine oder eine kleine Anzahl Zeilen benötigen.

SELECT TOP 1 * FROM [table] 
TABLESAMPLE (500 ROWS) 
ORDER BY NEWID()
 SELECT * FROM table ORDER BY RAND() LIMIT 1

Ich muss CD-MaN zustimmen:Die Verwendung von „ORDER BY RAND()“ funktioniert gut für kleine Tabellen oder wenn Sie Ihre SELECT-Anweisung nur ein paar Mal ausführen.

Ich verwende auch die Technik „num_value >= RAND() * ...“, und wenn ich wirklich zufällige Ergebnisse haben möchte, habe ich eine spezielle „zufällige“ Spalte in der Tabelle, die ich etwa einmal am Tag aktualisiere.Dieser einzelne UPDATE-Lauf wird einige Zeit in Anspruch nehmen (insbesondere, weil Sie einen Index für diese Spalte benötigen), aber er ist viel schneller, als bei jeder Ausführung der Auswahl Zufallszahlen für jede Zeile zu erstellen.

Seien Sie vorsichtig, da TableSample eigentlich keine zufällige Stichprobe von Zeilen zurückgibt.Dadurch wird Ihre Abfrage angewiesen, sich eine zufällige Stichprobe der 8-KB-Seiten anzusehen, aus denen Ihre Zeile besteht.Anschließend wird Ihre Abfrage anhand der auf diesen Seiten enthaltenen Daten ausgeführt.Aufgrund der Gruppierung der Daten auf diesen Seiten (Einfügungsreihenfolge usw.) kann es sein, dass es sich bei den Daten nicht um eine Zufallsstichprobe handelt.

Sehen: http://www.mssqltips.com/tip.asp?tip=1308

Diese MSDN-Seite für TableSample enthält ein Beispiel dafür, wie eine tatsächlich zufällige Stichprobe von Daten generiert wird.

http://msdn.microsoft.com/en-us/library/ms189108.aspx

Es scheint, dass viele der aufgeführten 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 ermitteln, 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 FROM TABLE)
SELECT COLUMN FROM TABLE WHERE IDX > .5
FETCH FIRST 1 ROW ONLY

Ein einfacher und effizienter Weg von http://akinas.com/pages/en/blog/mysql_random_row/

SET @i = (SELECT FLOOR(RAND() * COUNT(*)) FROM table); PREPARE get_stmt FROM 'SELECT * FROM table LIMIT ?, 1'; EXECUTE get_stmt USING @i;

Es gibt eine bessere Lösung für Oracle als die Verwendung von dbms_random.value, obwohl es einen vollständigen Scan erfordert, um Zeilen nach dbms_random.value zu sortieren, und bei großen Tabellen ziemlich langsam ist.

Verwenden Sie stattdessen Folgendes:

SELECT *
FROM employee sample(1)
WHERE rownum=1

Für Firebird:

Select FIRST 1 column from table ORDER BY RAND()

Mit SQL Server 2012+ können Sie das verwenden OFFSET FETCH-Abfrage um dies für eine einzelne zufällige Zeile zu tun

select  * from MyTable ORDER BY id OFFSET n ROW FETCH NEXT 1 ROWS 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 bei Lücken in den Tabellendaten, sofern Sie über einen Index verfügen, mit dem Sie für die ORDER BY-Klausel arbeiten können.Es ist auch sehr gut für die Zufälligkeit – da Sie das selbst herausfinden, um es weiterzugeben, aber die Probleme anderer Methoden sind nicht vorhanden.Darüber hinaus ist die Leistung ziemlich gut, bei einem kleineren Datensatz hält sie gut, obwohl ich keine ernsthaften Leistungstests mit mehreren Millionen Zeilen durchgeführt habe.

Für SQL Server 2005 und höher wird die Antwort von @GreyPanther für die Fälle erweitert, in denen num_value hat keine kontinuierlichen Werte.Dies funktioniert auch in Fällen, in denen wir Datensätze nicht gleichmäßig verteilt haben und wenn num_value ist keine Zahl, sondern eine eindeutige Kennung.

WITH CTE_Table (SelRow, num_value) 
AS 
(
    SELECT ROW_NUMBER() OVER(ORDER BY ID) AS SelRow, num_value FROM table
) 

SELECT * FROM table Where num_value = ( 
    SELECT TOP 1 num_value FROM CTE_Table  WHERE SelRow >= RAND() * (SELECT MAX(SelRow) FROM CTE_Table)
)

Eine Zufallsfunktion aus dem SQL könnte helfen.Wenn Sie sich auf nur eine Zeile beschränken möchten, fügen Sie diese einfach am Ende hinzu.

SELECT column FROM table
ORDER BY RAND()
LIMIT 1
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top