通过SQLAlchemy获取随机行


Answers:


122

这在很大程度上是特定于数据库的问题。

我知道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中,选择随机记录会带来严重的性能问题。是一篇很好的文章。


11
+1。与适用于SQLite的Postgres相同: select.order_by(func.random()).limit(n)
mechanical_meat,2010年

您可以在Oracle中使用order_by('dbms_random.value')。
Buttons840

11
如果您使用声明性模型:session.query(MyModel).order_by(func.rand()).first
13年

2
感谢@trinth,当我在最后添加括号时,它起作用了:session.query(MyModel).order_by(func.rand()).first()
Kent Munthe Caspersen 2015年

3
从SQLAlchemy v0.4开始,它func.random()是一个通用函数,可编译为数据库的随机实现。
RazerM '16

25

如果您使用的是orm且表不大(或者您已缓存了其行数),并且希望它独立于数据库,那么真正简单的方法就是。

import random
rand = random.randrange(0, session.query(Table).count()) 
row = session.query(Table)[rand]

这有点作弊,但这就是为什么要使用orm。


rand = random.randrange(0,session.query(Table).count())
James Brady

您先选择并创建所有对象,然后再选择其中之一
SergeK。16年

怎么random.choice(session.query(Table))
所罗门·乌科

23

有一种简单的方法可以拉出独立于IS数据库的随机行。只需使用.offset()即可。无需拉所有行:

import random
query = DBSession.query(Table)
rowCount = int(query.count())
randomRow = query.offset(int(rowCount*random.random())).first()

Table是您的表(或者您可以在其中放置任何查询)。如果需要几行,则可以多次运行,并确保每行与上一行都不相同。


更新-在mysql中大约1000万行中,这实际上开始变得有点慢,我想您可以对其进行优化。
GuySoft

1
在约50万行的设置中对我来说效果很好。
Mario

1
现在在Oracle上达到了1100万行。...不再是那么好了:-)线性降级,但是...我还必须找到其他东西。
马里奥

2
@Jayme:您可以使用query.offset(random.randrange(rowCount)).limit(1).first()
jfs

1
@Jayme也有,.limit(1)以前有使用的理由.first()吗?似乎多余。也许query.offset(random.randrange(row_count)).first()就足够了。
jfs 2016年

17

这是四个不同的变体,从最慢到最快排序。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需要进行全表扫描而COUNTinoptimized_random()可以使用索引将大大降低。


采样品呢?喜欢 random.sample()做什么?这里有什么优化方式?
hamidfzm 2016年

打开一个新问题并链接到该问题,我将竭力回答。如果可能,请指定SQL的基本风格,因为这也会影响答案。
Jeff Widman

这不是在使用flask-sqlalchemy吗?
MattSom

3

一些SQL DBMS(即Microsoft SQL Server,DB2和PostgreSQL)已经实现了SQL:2003TABLESAMPLE子句。支持已添加到SQLAlchemy 1.1版中。它允许使用不同的采样方法返回表格的样本–标准要求SYSTEMBERNOULLI,这将返回所需的表格近似百分比。

在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采样方法之前,应该知道它是对页面而不是单个元组进行采样的,因此,它可能不适合例如小表,并且如果表是群集的,则可能不会产生随机结果。


0

这是我使用的解决方案:

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

1
在大桌子上,这将非常慢。您将抓住每一行,然后将其切成薄片。
马修(Matthew)

1
哇,这不是很好。如果存在查询以获取表记录计数,那将是一个更好的方法。这是在带有小型数据库的Web应用程序上完成的,不再与该公司合作,因此我对此无能为力。
ChickenFeet

0

这是我选择表的随机行的功能:

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()

-1

在从数据库中选择随机问题的示例中,使用此最简单的方法:-

#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())

1.如果数据库中有一百万条记录怎么办?2.我们应该全部拿走并随机选择一个吗?会不会是一个昂贵的电话?
Sourav Badami

1
绝对会是一个昂贵的电话,但他只要求使用随机方法,而不是问“如何使用特定范围的数据或通过特定的键进行随机查询”,因此,如果我回答并考虑了您所说的内容,那将是完全不同的话题。我尽力回答了尽可能简单的问题,因此它很清楚,仅用于确切的询问。人们可以用很多行来回答,而这可能更简单。
阿纳斯

-2

该解决方案将选择一个随机行

此解决方案要求主键名为id,如果尚未指定,则应为:

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

4
如果您的身份证号码有间隔,此操作将失败。
erickrf

-6

通过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

但是我不知道任何标准方法


7
是的 我知道如何在SQL中执行此操作(我在beta.stackoverflow.com/questions/19412/…中发布了该答案),但正在寻找SQLAlchemy特定的解决方案。
08年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.