如何使用SQLAlchemy从表中选择一个或多个随机行?
Answers:
这在很大程度上是特定于数据库的问题。
我知道PostgreSQL,SQLite,MySQL和Oracle可以通过随机函数进行排序,因此可以在SQLAlchemy中使用它:
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
接下来,您需要通过所需的记录数来限制查询(例如使用.limit()
)。
请记住,至少在PostgreSQL中,选择随机记录会带来严重的性能问题。这是一篇很好的文章。
session.query(MyModel).order_by(func.rand()).first
session.query(MyModel).order_by(func.rand()).first()
func.random()
是一个通用函数,可编译为数据库的随机实现。
如果您使用的是orm且表不大(或者您已缓存了其行数),并且希望它独立于数据库,那么真正简单的方法就是。
import random
rand = random.randrange(0, session.query(Table).count())
row = session.query(Table)[rand]
这有点作弊,但这就是为什么要使用orm。
random.choice(session.query(Table))
样
有一种简单的方法可以拉出独立于IS数据库的随机行。只需使用.offset()即可。无需拉所有行:
import random
query = DBSession.query(Table)
rowCount = int(query.count())
randomRow = query.offset(int(rowCount*random.random())).first()
Table是您的表(或者您可以在其中放置任何查询)。如果需要几行,则可以多次运行,并确保每行与上一行都不相同。
query.offset(random.randrange(rowCount)).limit(1).first()
。
.limit(1)
以前有使用的理由.first()
吗?似乎多余。也许query.offset(random.randrange(row_count)).first()
就足够了。
这是四个不同的变体,从最慢到最快排序。timeit
结果在底部:
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
10,000的结果在Macbook上针对300行的PostgreSQL表运行:
simple_random():
90.09954111799925
load_only_random():
65.94714171699889
order_by_random():
23.17819356000109
optimized_random():
19.87806927999918
您可以轻松地看到,使用func.random()
远远快于将所有结果返回给Python的random.choice()
。
此外,随着表格大小的增加, order_by_random()
由于anORDER BY
需要进行全表扫描而COUNT
inoptimized_random()
可以使用索引将大大降低。
random.sample()
做什么?这里有什么优化方式?
flask-sqlalchemy
吗?
一些SQL DBMS(即Microsoft SQL Server,DB2和PostgreSQL)已经实现了SQL:2003TABLESAMPLE
子句。支持已添加到SQLAlchemy 1.1版中。它允许使用不同的采样方法返回表格的样本–标准要求SYSTEM
和BERNOULLI
,这将返回所需的表格近似百分比。
在SQLAlchemy中FromClause.tablesample()
,tablesample()
用于生成TableSample
构造:
# Approx. 1%, using SYSTEM method
sample1 = mytable.tablesample(1)
# Approx. 1%, using BERNOULLI method
sample2 = mytable.tablesample(func.bernoulli(1))
与映射类一起使用时,会有些许困惑:TableSample
必须对产生的对象进行别名处理才能用于查询模型对象:
sample = aliased(MyModel, tablesample(MyModel, 1))
res = session.query(sample).all()
由于许多答案都包含性能基准,因此我还将在此处包括一些简单的测试。使用PostgreSQL中具有约一百万行和一个整数列的简单表,选择(大约)1%的样本:
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)
在急于使用SYSTEM
采样方法之前,应该知道它是对页面而不是单个元组进行采样的,因此,它可能不适合例如小表,并且如果表是群集的,则可能不会产生随机结果。
这是我使用的解决方案:
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
在从数据库中选择随机问题的示例中,使用此最简单的方法:-
#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())
通过SQL有几种方法,具体取决于所使用的数据库。
(我认为SQLAlchemy仍然可以使用所有这些方法)
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
甲骨文:
SELECT column FROM
(SELECT column FROM table
ORDER BY dbms_random.value)
WHERE rownum = 1
但是我不知道任何标准方法
select.order_by(func.random()).limit(n)