Ich habe eine sqlite
Tabelle mit folgendem Schema:
CREATE TABLE foo (bar VARCHAR)
Ich verwende diese Tabelle als Speicher für eine Liste von Zeichenfolgen.
Wie wähle ich eine zufällige Zeile aus dieser Tabelle aus?
Ich habe eine sqlite
Tabelle mit folgendem Schema:
CREATE TABLE foo (bar VARCHAR)
Ich verwende diese Tabelle als Speicher für eine Liste von Zeichenfolgen.
Wie wähle ich eine zufällige Zeile aus dieser Tabelle aus?
Antworten:
Schauen Sie sich das Auswählen einer zufälligen Zeile aus einer SQLite-Tabelle an
SELECT * FROM table ORDER BY RANDOM() LIMIT 1;
SELECT a.foo FROM a JOIN b ON a.id = b.id WHERE b.bar = 2 ORDER BY RANDOM() LIMIT 1;
bekomme ich immer die gleiche Zeile.
Die folgenden Lösungen sind viel schneller als die von anktastic (die Anzahl (*) kostet viel, aber wenn Sie sie zwischenspeichern können, sollte der Unterschied nicht so groß sein), was selbst viel schneller ist als die "order by random ()" wenn Sie eine große Anzahl von Zeilen haben, obwohl sie einige Unannehmlichkeiten haben.
Wenn Ihre Rowids ziemlich voll sind (dh nur wenige Löschungen), können Sie Folgendes tun (verwenden Sie (select max(rowid) from foo)+1
anstelle von, um max(rowid)+1
eine bessere Leistung zu erzielen, wie in den Kommentaren erläutert):
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Wenn Sie Löcher haben, versuchen Sie manchmal, eine nicht vorhandene Zeilen-ID auszuwählen, und die Auswahl gibt eine leere Ergebnismenge zurück. Wenn dies nicht akzeptabel ist, können Sie einen Standardwert wie den folgenden angeben:
select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1)) or rowid = (select max(rowid) from node) order by rowid limit 1;
Diese zweite Lösung ist nicht perfekt: Die Wahrscheinlichkeitsverteilung ist in der letzten Zeile (der mit der höchsten Zeilen-ID) höher. Wenn Sie jedoch häufig Daten zur Tabelle hinzufügen, wird dies zu einem sich bewegenden Ziel, und die Verteilung der Wahrscheinlichkeiten sollte höher sein viel besser.
Eine weitere Lösung: Wenn Sie häufig zufällige Elemente aus einer Tabelle mit vielen Löchern auswählen, möchten Sie möglicherweise eine Tabelle erstellen, die die Zeilen der ursprünglichen Tabelle enthält, die in zufälliger Reihenfolge sortiert sind:
create table random_foo(foo_id);
Füllen Sie dann regelmäßig die Tabelle random_foo neu aus
delete from random_foo;
insert into random_foo select id from foo;
Und um eine zufällige Zeile auszuwählen, können Sie meine erste Methode verwenden (hier gibt es keine Löcher). Natürlich hat diese letzte Methode einige Parallelitätsprobleme, aber die Neuerstellung von random_foo ist eine Wartungsoperation, die wahrscheinlich nicht sehr häufig vorkommt.
Eine weitere Möglichkeit, die ich kürzlich auf einer Mailingliste gefunden habe , besteht darin, beim Löschen einen Auslöser zu setzen, um die Zeile mit der größten Zeilen-ID in die aktuell gelöschte Zeile zu verschieben, sodass keine Löcher mehr vorhanden sind.
Beachten Sie zum Schluss, dass das Verhalten der automatischen Inkrementierung von Zeilen-ID und ganzzahligem Primärschlüssel nicht identisch ist (bei Zeilen-ID wird beim Einfügen einer neuen Zeile max (Zeilen-ID) +1 ausgewählt, wobei es der höchste Wert ist, der jemals gesehen wurde + 1 für ein Primärschlüssel), sodass die letzte Lösung nicht mit einer automatischen Inkrementierung in random_foo funktioniert, die anderen Methoden jedoch.
SELECT max(rowid) + 1
wird eine langsame Abfrage sein - es erfordert einen vollständigen Tabellenscan. SQLite optimiert nur die Abfrage SELECT max(rowid)
. Daher würde diese Antwort verbessert werden durch: select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));
Weitere Informationen finden Sie hier: sqlite.1065341.n5.nabble.com/…
Sie müssen "order by RANDOM ()" in Ihre Abfrage aufnehmen.
Beispiel:
select * from quest order by RANDOM();
Sehen wir uns ein vollständiges Beispiel an
CREATE TABLE quest (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quest TEXT NOT NULL,
resp_id INTEGER NOT NULL
);
Einfügen einiger Werte:
insert into quest(quest, resp_id) values ('1024/4',6), ('256/2',12), ('128/1',24);
Eine Standardauswahl:
select * from quest;
| id | quest | resp_id |
1 1024/4 6
2 256/2 12
3 128/1 24
--
Ein ausgewählter Zufall:
select * from quest order by RANDOM();
| id | quest | resp_id |
3 128/1 24
1 1024/4 6
2 256/2 12
--
* Bei jeder Auswahl ist die Reihenfolge anders.
Wenn Sie nur eine Zeile zurückgeben möchten
select * from quest order by RANDOM() LIMIT 1;
| id | quest | resp_id |
2 256/2 12
--
* Bei jeder Auswahl ist die Rückgabe anders.
Wie wäre es mit:
SELECT COUNT(*) AS n FROM foo;
Wählen Sie dann eine Zufallszahl m in [0, n) und
SELECT * FROM foo LIMIT 1 OFFSET m;
Sie können die erste Nummer ( n ) sogar irgendwo speichern und nur aktualisieren, wenn sich die Datenbankanzahl ändert. Auf diese Weise müssen Sie nicht jedes Mal SELECT COUNT ausführen.
OFFSET
, abhängig von der Größe des Versatzes zu steigen scheint - Zeile 2 ist schnell, Zeile 2 Millionen dauert eine Weile, selbst wenn alle Daten in der festen Größe sind und es sollte in der Lage sein, direkt danach zu suchen. Zumindest sieht es in SQLite 3.7.13 so aus.
SELECT bar
FROM foo
ORDER BY Random()
LIMIT 1
Hier ist eine Modifikation der Lösung von @ ank:
SELECT *
FROM table
LIMIT 1
OFFSET ABS(RANDOM()) % MAX((SELECT COUNT(*) FROM table), 1)
Diese Lösung funktioniert auch für Indizes mit Lücken, da wir einen Offset in einem Bereich [0, count] randomisieren. MAX
wird verwendet, um einen Fall mit leerer Tabelle zu behandeln.
Hier sind einfache Testergebnisse für eine Tabelle mit 16.000 Zeilen:
sqlite> .timer on
sqlite> select count(*) from payment;
16049
Run Time: real 0.000 user 0.000140 sys 0.000117
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
14746
Run Time: real 0.002 user 0.000899 sys 0.000132
sqlite> select payment_id from payment limit 1 offset abs(random()) % (select count(*) from payment);
12486
Run Time: real 0.001 user 0.000952 sys 0.000103
sqlite> select payment_id from payment order by random() limit 1;
3134
Run Time: real 0.015 user 0.014022 sys 0.000309
sqlite> select payment_id from payment order by random() limit 1;
9407
Run Time: real 0.018 user 0.013757 sys 0.000208
Ich habe die folgende Lösung für die großen sqlite3-Datenbanken entwickelt :
SELECT * FROM foo WHERE rowid = abs(random()) % (SELECT max(rowid) FROM foo) + 1;
Die Funktion abs (X) gibt den Absolutwert des numerischen Arguments X zurück.
Die Funktion random () gibt eine pseudozufällige Ganzzahl zwischen -9223372036854775808 und +9223372036854775807 zurück.
Der Operator% gibt den ganzzahligen Wert seines linken Operanden modulo seines rechten Operanden aus.
Schließlich fügen Sie +1 hinzu, um zu verhindern, dass die Zeilen-ID gleich 0 ist.