Wie wähle ich mit SQLAlchemy eine oder mehrere zufällige Zeilen aus einer Tabelle aus?
81
Wie wähle ich mit SQLAlchemy eine oder mehrere zufällige Zeilen aus einer Tabelle aus?
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.
select.order_by(func.random()).limit(n)
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.
quelle
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.
quelle
query.offset(random.randrange(rowCount)).limit(1).first()
..limit(1)
vorher zu verwenden.first()
? Es scheint überflüssig. Vielleichtquery.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 Pythonrandom.choice()
.Zusätzlich ist , wie die Größe der Tabelle erhöht, die Leistung
order_by_random()
erheblich beeinträchtigt , da einORDER BY
einen vollständigen Tabellenscan gegenüber dem erfordertCOUNT
inoptimized_random()
kann einen Index verwenden.quelle
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 verlangtSYSTEM
undBERNOULLI
, die einen gewünschten ungefähren Prozentsatz einer Tabelle zurückgeben.In SQLAlchemy
FromClause.tablesample()
undtablesample()
werden verwendet, um einTableSample
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.quelle
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
quelle
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()
quelle
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())
quelle
Diese Lösung wählt eine einzelne zufällige Zeile aus
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
quelle
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
quelle