Wie kann ich in reinem SQL eine zufällige Zeile anfordern (oder eine, die dem echten Zufall so nahe wie möglich kommt)?
Antworten
Zu viele Anzeigen?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 Seed jedes Mal denselben Satz von Zeilen auswählen musste.
Für MS SQL:
Minimales Beispiel:
select top 10 percent *
from table_name
order by rand(checksum(*))
Normalisierte Ausführungszeit: 1.00
Beispiel NewId():
select top 10 percent *
from table_name
order by newid()
Normalisierte Ausführungszeit: 1,02
NewId()
ist nur unwesentlich langsamer als rand(checksum(*))
Daher sollten Sie es nicht für große Datensätze verwenden.
Auswahl mit Initialseed:
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 die gleiche Menge bei einem Saatgut auswählen müssen, scheint dies zu funktionieren.
Die meisten der hier vorgestellten Lösungen zielen darauf ab, das Sortieren zu vermeiden, aber sie müssen immer noch einen sequentiellen Scan über eine Tabelle durchführen.
Es gibt auch eine Möglichkeit, die sequentielle Suche zu vermeiden, indem man zur Indexsuche übergeht. Wenn Sie den Indexwert Ihrer zufälligen Zeile kennen, können Sie das Ergebnis fast sofort erhalten. Das Problem ist, wie man einen Indexwert errät.
Die folgende Lösung funktioniert mit 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;
Bei 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 auch 100 oder 1000 verwenden, da dies (erstaunlicherweise) keinen großen Einfluss auf die Antwortzeit hat.
Es gibt auch ein Problem - wenn Sie spärliche IDs haben die Sie vermissen könnten . Die Lösung besteht darin einen Notfallplan haben :) In diesem Fall eine reine alte order by random()-Abfrage. Wenn kombiniert id sieht wie folgt aus:
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 die Gewerkschaft ALLE Klausel. In diesem Fall, wenn der erste Teil Daten zurückgibt, wird der zweite Teil NIE ausgeführt!
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;