Wie wähle ich mit SQLAlchemy eine oder mehrere zufällige Zeilen aus einer Tabelle aus?
Antworten:
Dies ist ein sehr datenbankspezifisches Problem.
Ich weiß, dass PostgreSQL, SQLite, MySQL und Oracle die Möglichkeit haben, nach einer Zufallsfunktion zu ordnen, sodass Sie dies in SQLAlchemy verwenden können:
from sqlalchemy.sql.expression import func, select
select.order_by(func.random()) # for PostgreSQL, SQLite
select.order_by(func.rand()) # for MySQL
select.order_by('dbms_random.value') # For Oracle
Als Nächstes müssen Sie die Abfrage durch die Anzahl der benötigten Datensätze begrenzen (z. B. mithilfe von .limit()
).
Beachten Sie, dass zumindest in PostgreSQL die Auswahl von Zufallsdatensätzen schwerwiegende Leistungsprobleme aufweist. Hier ist ein guter Artikel darüber.
session.query(MyModel).order_by(func.rand()).first
session.query(MyModel).order_by(func.rand()).first()
func.random()
handelt es sich um eine generische Funktion, die zur zufälligen Implementierung der Datenbank kompiliert wird.
Wenn Sie den Orm verwenden und die Tabelle nicht groß ist (oder wenn die Anzahl der Zeilen zwischengespeichert ist) und Sie möchten, dass sie datenbankunabhängig ist, ist der wirklich einfache Ansatz.
import random
rand = random.randrange(0, session.query(Table).count())
row = session.query(Table)[rand]
Dies betrügt leicht, aber deshalb verwenden Sie einen Orm.
random.choice(session.query(Table))
?
Es gibt eine einfache Möglichkeit, eine zufällige Zeile abzurufen, die datenbankunabhängig ist. Verwenden Sie einfach .offset (). Sie müssen nicht alle Zeilen ziehen:
import random
query = DBSession.query(Table)
rowCount = int(query.count())
randomRow = query.offset(int(rowCount*random.random())).first()
Wobei Tabelle Ihre Tabelle ist (oder Sie könnten dort eine Abfrage stellen). Wenn Sie einige Zeilen möchten, können Sie diese einfach mehrmals ausführen und sicherstellen, dass nicht jede Zeile mit der vorherigen identisch ist.
query.offset(random.randrange(rowCount)).limit(1).first()
.
.limit(1)
vorher zu verwenden .first()
? Es scheint überflüssig. Vielleicht query.offset(random.randrange(row_count)).first()
ist genug.
Hier sind vier verschiedene Varianten, geordnet von der langsamsten zur schnellsten. timeit
Ergebnisse unten:
from sqlalchemy.sql import func
from sqlalchemy.orm import load_only
def simple_random():
return random.choice(model_name.query.all())
def load_only_random():
return random.choice(model_name.query.options(load_only('id')).all())
def order_by_random():
return model_name.query.order_by(func.random()).first()
def optimized_random():
return model_name.query.options(load_only('id')).offset(
func.floor(
func.random() *
db.session.query(func.count(model_name.id))
)
).limit(1).all()
timeit
Ergebnisse für 10.000 Läufe auf meinem Macbook gegen eine PostgreSQL-Tabelle mit 300 Zeilen:
simple_random():
90.09954111799925
load_only_random():
65.94714171699889
order_by_random():
23.17819356000109
optimized_random():
19.87806927999918
Sie können leicht erkennen, dass die Verwendung func.random()
weitaus schneller ist als die Rückgabe aller Ergebnisse an Python random.choice()
.
Zusätzlich ist , wie die Größe der Tabelle erhöht, die Leistung order_by_random()
erheblich beeinträchtigt , da ein ORDER BY
einen vollständigen Tabellenscan gegenüber dem erfordert COUNT
in optimized_random()
kann einen Index verwenden.
random.sample()
tun? Was ist hier optimiert?
flask-sqlalchemy
?
Einige SQL-DBMS, nämlich Microsoft SQL Server, DB2 und PostgreSQL, haben die SQL: 2003- TABLESAMPLE
Klausel implementiert . SQLAlchemy wurde in Version 1.1 unterstützt . Es ermöglicht die Rückgabe einer Stichprobe einer Tabelle mit verschiedenen Stichprobenmethoden - der Standard verlangt SYSTEM
und BERNOULLI
, die einen gewünschten ungefähren Prozentsatz einer Tabelle zurückgeben.
In SQLAlchemy FromClause.tablesample()
und tablesample()
werden verwendet, um ein TableSample
Konstrukt herzustellen :
# Approx. 1%, using SYSTEM method
sample1 = mytable.tablesample(1)
# Approx. 1%, using BERNOULLI method
sample2 = mytable.tablesample(func.bernoulli(1))
Bei Verwendung mit zugeordneten Klassen gibt TableSample
es ein kleines Problem : Das erzeugte Objekt muss mit einem Alias versehen sein, um Modellobjekte abfragen zu können:
sample = aliased(MyModel, tablesample(MyModel, 1))
res = session.query(sample).all()
Da viele der Antworten Leistungsbenchmarks enthalten, werde ich auch hier einige einfache Tests einfügen. Wählen Sie anhand einer einfachen Tabelle in PostgreSQL mit etwa einer Million Zeilen und einer einzelnen Ganzzahlspalte (ca.) 1% Stichprobe aus:
In [24]: %%timeit
...: foo.select().\
...: order_by(func.random()).\
...: limit(select([func.round(func.count() * 0.01)]).
...: select_from(foo).
...: as_scalar()).\
...: execute().\
...: fetchall()
...:
307 ms ± 5.72 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [25]: %timeit foo.tablesample(1).select().execute().fetchall()
6.36 ms ± 188 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [26]: %timeit foo.tablesample(func.bernoulli(1)).select().execute().fetchall()
19.8 ms ± 381 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Bevor Sie sich beeilen, die SYSTEM
Stichprobenmethode zu verwenden, sollten Sie wissen, dass Seiten und nicht einzelne Tupel abgetastet werden. Daher ist sie möglicherweise nicht für kleine Tabellen geeignet und führt möglicherweise nicht zu zufälligen Ergebnissen, wenn die Tabelle gruppiert ist.
Dies ist die Lösung, die ich benutze:
from random import randint
rows_query = session.query(Table) # get all rows
if rows_query.count() > 0: # make sure there's at least 1 row
rand_index = randint(0,rows_query.count()-1) # get random index to rows
rand_row = rows_query.all()[rand_index] # use random index to get random row
Dies ist meine Funktion, um zufällige Zeilen einer Tabelle auszuwählen:
from sqlalchemy.sql.expression import func
def random_find_rows(sample_num):
if not sample_num:
return []
session = DBSession()
return session.query(Table).order_by(func.random()).limit(sample_num).all()
Verwenden Sie diese einfachste Methode in diesem Beispiel, um eine zufällige Frage aus der Datenbank auszuwählen: -
#first import the random module
import random
#then choose what ever Model you want inside random.choise() method
get_questions = random.choice(Question.query.all())
Diese Lösung erfordert, dass der Primärschlüssel den Namen id trägt. Wenn dies nicht bereits geschehen ist, sollte dies der Fall sein:
import random
max_model_id = YourModel.query.order_by(YourModel.id.desc())[0].id
random_id = random.randrange(0,max_model_id)
random_row = YourModel.query.get(random_id)
print random_row
Je nachdem, welche Datenbank verwendet wird, gibt es verschiedene Möglichkeiten für SQL.
(Ich denke, SQLAlchemy kann all dies sowieso nutzen)
MySQL:
SELECT colum FROM table
ORDER BY RAND()
LIMIT 1
PostgreSQL:
SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1
MSSQL:
SELECT TOP 1 column FROM table
ORDER BY NEWID()
IBM DB2:
SELECT column, RAND() as IDX
FROM table
ORDER BY IDX FETCH FIRST 1 ROWS ONLY
Orakel:
SELECT column FROM
(SELECT column FROM table
ORDER BY dbms_random.value)
WHERE rownum = 1
Ich kenne jedoch keinen Standardweg
select.order_by(func.random()).limit(n)