通过SQLAlchemy获取随机行

103 投票
9 回答
55812 浏览
提问于 2025-04-11 09:24

我该如何使用SQLAlchemy从表中随机选择一行或多行数据呢?

9 个回答

27

如果你在使用ORM(对象关系映射)并且表格不大(或者你已经缓存了行数),而且你希望它能独立于数据库,那么一个非常简单的方法就是。

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

这样做有点偷懒,但这就是使用ORM的原因。

31

这里有四种不同的方式,从最慢到最快排列。下面是用 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()

这是我在Macbook上对一个有300行的PostgreSQL表进行10,000次运行的 timeit 结果:

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() 的性能会显著下降,因为 ORDER BY 需要对整个表进行扫描,而 optimized_random() 中的 COUNT 可以利用索引。

164

这个问题主要跟数据库有关。

我知道像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中,选择随机记录会有很大的性能问题;这里有一篇不错的文章讲解这个问题。

撰写回答