Pregunta

¿Cómo seleccionar una(o algunas) fila al azar(s) a partir de una tabla con SQLAlchemy?

¿Fue útil?

Solución

Esta es una base de datos específica de la cuestión.

Sé que PostgreSQL, SQLite, MySQL y Oracle tienen la capacidad para realizar un pedido por una función aleatoria, así que usted puede utilizar 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

Después, usted necesita para limitar la consulta por el número de registros que necesita (por ejemplo el uso de .limit()).

Tenga en cuenta que al menos en PostgreSQL, la selección de registro aleatorio tiene graves problemas de perfomance; aquí es bueno el artículo sobre él.

Otros consejos

Si usted está utilizando el orm y la tabla no es grande (o su cantidad de filas en caché) y desea que sea la base de datos independiente de la que realmente enfoque simple.

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

Esta es la trampa un poco, pero es por eso que el uso de un orm.

Hay una manera sencilla de sacar una fila al azar que ES la base de datos independiente.Sólo uso .offset() .No hay necesidad de tirar todas las filas:

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

Donde Tabla es la tabla (o usted podría poner cualquier consulta no).Si quieres un par de filas, entonces usted puede simplemente ejecutar esto varias veces, y asegúrese de que cada fila no es idéntica a la anterior.

Aquí hay cuatro versiones diferentes, ordenados del más lento al más rápido. 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 ejecuta en mi Macbook en contra de 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

Usted puede ver fácilmente que el uso de func.random() es mucho más rápido que devolver todos los resultados para Python random.choice().

Además, como el tamaño de la tabla aumenta, el rendimiento de order_by_random() se degradan significativamente debido a una ORDER BY requiere de un análisis completo de la tabla frente a la COUNT en optimized_random() puede utilizar un índice.

Algunos de SQL DBMS, es decir, Microsoft SQL Server, DB2, y PostgreSQL han implementado el SQL:2003 TABLESAMPLE la cláusula.Se ha añadido soporte para SQLAlchemy en la versión 1.1.Se permite la devolución de un ejemplo de una tabla utilizando diferentes métodos de muestreo – el estándar requiere SYSTEM y BERNOULLI, que generan un porcentaje aproximado de una tabla.

En SQLAlchemy FromClause.tablesample() y tablesample() se utilizan para producir una TableSample construir:

# 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 utiliza con asignan clases:la producción de TableSample el objeto debe ser suavizado con el fin de ser utilizada para la consulta de los objetos del modelo:

sample = aliased(MyModel, tablesample(MyModel, 1))
res = session.query(sample).all()

Dado que muchas de las respuestas contienen los criterios de desempeño, voy a incluir algunas pruebas sencillas aquí.Mediante una sencilla tabla en PostgreSQL con cerca de un millón de filas y una columna de tipo entero, seleccione (aprox.) 1% de la 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 utilizar SYSTEM método de muestreo que uno debe saber que las muestras de páginas, no individual tuplas, por lo que podría no ser adecuado para las pequeñas mesas, por ejemplo.

Esta es la solución que yo 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

Este es mi función para seleccionar al azar la fila(s) 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()

esta solución se selecciona una sola fila al azar

Esta solución requiere que la clave principal se denomina id, debe ser que si no ya:

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 maneras a través de SQL, dependiendo de la base de datos está siendo utilizada.

(Creo que SQLAlchemy puede utilizar 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

Oracle:

SELECT column FROM
(SELECT column FROM table
ORDER BY dbms_random.value)
WHERE rownum = 1

Sin embargo, yo no sé de ningún modo estándar

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top