¿Cómo selecciono una o más filas aleatorias de una tabla usando SQLAlchemy?
Respuestas:
Este es un problema muy específico de la base de datos.
Sé que PostgreSQL, SQLite, MySQL y Oracle tienen la capacidad de ordenar por una función aleatoria, por lo que puede usar esto en 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
A continuación, debe limitar la consulta por la cantidad de registros que necesita (por ejemplo, usar .limit()
).
Tenga en cuenta que al menos en PostgreSQL, la selección de registros aleatorios tiene graves problemas de rendimiento; aquí hay un buen artículo al respecto.
session.query(MyModel).order_by(func.rand()).first
session.query(MyModel).order_by(func.rand()).first()
func.random()
es una función genérica que se compila a la implementación aleatoria de la base de datos.
Si está utilizando el orm y la tabla no es grande (o tiene su cantidad de filas almacenadas en caché) y desea que sea independiente de la base de datos, el enfoque realmente simple es.
import random
rand = random.randrange(0, session.query(Table).count())
row = session.query(Table)[rand]
Esto es un poco de trampa, pero por eso usas un orm.
random.choice(session.query(Table))
?
Hay una forma sencilla de extraer una fila aleatoria que ES independiente de la base de datos. Simplemente use .offset (). No es necesario tirar de todas las filas:
import random
query = DBSession.query(Table)
rowCount = int(query.count())
randomRow = query.offset(int(rowCount*random.random())).first()
Donde Table es su tabla (o puede poner cualquier consulta allí). Si desea algunas filas, puede ejecutar esto varias veces y asegurarse de que cada fila no sea idéntica a la anterior.
query.offset(random.randrange(rowCount)).limit(1).first()
.
.limit(1)
antes .first()
? Parece redundante. Quizás query.offset(random.randrange(row_count)).first()
sea suficiente.
Aquí hay cuatro variaciones diferentes, ordenadas de la más lenta a la más rápida. timeit
resultados en la parte inferior:
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
resultados para 10,000 ejecuciones en mi Macbook contra una tabla de PostgreSQL con 300 filas:
simple_random():
90.09954111799925
load_only_random():
65.94714171699889
order_by_random():
23.17819356000109
optimized_random():
19.87806927999918
Puede ver fácilmente que usar func.random()
es mucho más rápido que devolver todos los resultados a Python random.choice()
.
Además, como el tamaño de la tabla aumenta, el rendimiento de order_by_random()
degradará significativamente debido a una ORDER BY
requiere un escaneo completo de tabla frente a la COUNT
en optimized_random()
puede utilizar un índice.
random.sample()
hacer ¿Qué es la forma optimizada aquí?
flask-sqlalchemy
?
Algunos DBMS SQL, a saber, Microsoft SQL Server, DB2 y PostgreSQL han implementado la TABLESAMPLE
cláusula SQL: 2003 . Se agregó soporte a SQLAlchemy en la versión 1.1 . Permite devolver una muestra de una tabla utilizando diferentes métodos de muestreo: el estándar requiere SYSTEM
y BERNOULLI
, que devuelven un porcentaje aproximado deseado de una tabla.
En SQLAlchemy FromClause.tablesample()
y tablesample()
se utilizan para producir una TableSample
construcción:
# Approx. 1%, using SYSTEM method
sample1 = mytable.tablesample(1)
# Approx. 1%, using BERNOULLI method
sample2 = mytable.tablesample(func.bernoulli(1))
Hay un pequeño problema cuando se usa con clases mapeadas: el TableSample
objeto producido debe tener un alias para poder usarlo para consultar objetos de modelo:
sample = aliased(MyModel, tablesample(MyModel, 1))
res = session.query(sample).all()
Dado que muchas de las respuestas contienen evaluaciones comparativas de rendimiento, también incluiré aquí algunas pruebas sencillas. Usando una tabla simple en PostgreSQL con aproximadamente un millón de filas y una sola columna entera, seleccione (aprox.) 1% de muestra:
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)
Antes de apresurarse a usar SYSTEM
el método de muestreo, uno debe saber que toma muestras de páginas , no de tuplas individuales, por lo que podría no ser adecuado para tablas pequeñas, por ejemplo, y puede que no produzca resultados aleatorios si la tabla está agrupada.
Esta es la solución que uso:
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
Esta es mi función para seleccionar filas aleatorias de una tabla:
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()
Utilice este método más simple en este ejemplo para elegir una pregunta aleatoria de la base de datos: -
#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())
Esta solución requiere que la clave principal se llame id, debería serlo si aún no lo está:
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
Hay un par de formas a través de SQL, según la base de datos que se utilice.
(Creo que SQLAlchemy puede usar todos estos de todos modos)
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
Oráculo:
SELECT column FROM
(SELECT column FROM table
ORDER BY dbms_random.value)
WHERE rownum = 1
Sin embargo, no conozco ninguna forma estándar
select.order_by(func.random()).limit(n)