¿Cómo selecciono una o más filas aleatorias de una tabla usando SQLAlchemy?
81
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.
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()
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.
fuente
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.
fuente
query.offset(random.randrange(rowCount)).limit(1).first()
..limit(1)
antes.first()
? Parece redundante. Quizásquery.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 Pythonrandom.choice()
.Además, como el tamaño de la tabla aumenta, el rendimiento de
order_by_random()
degradará significativamente debido a unaORDER BY
requiere un escaneo completo de tabla frente a laCOUNT
enoptimized_random()
puede utilizar un índice.fuente
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 requiereSYSTEM
yBERNOULLI
, que devuelven un porcentaje aproximado deseado de una tabla.En SQLAlchemy
FromClause.tablesample()
ytablesample()
se utilizan para producir unaTableSample
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.fuente
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
fuente
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()
fuente
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())
fuente
esta solución seleccionará una sola fila aleatoria
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
fuente
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
fuente