Получение случайной строки через SQLAlchemy


Как выбрать (или некоторые) случайные строки из таблицы с помощью SQLAlchemy?

9 58

9 ответов:

Это очень специфическая для базы данных проблема.

Я знаю, что 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 выбор случайной записи имеет серьезные проблемы с производительностью; здесь хорошая статья о оно.

Если вы используете orm и таблица не большая (или у вас есть ее количество строк в кэше), и вы хотите, чтобы она была независимой от базы данных, действительно простой подход.

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

это немного обманывает, но вот почему вы используете orm.

существует простой способ вытащить случайную строку, которая не зависит от базы данных. Просто использовать. смещение. )( Не нужно тянуть все строки:

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

где таблица - это ваша таблица (или вы можете поместить туда любой запрос). Если вы хотите несколько строк, то вы можете просто запустить это несколько раз и убедитесь, что каждая строка не совпадает с предыдущим.

вот четыре различных вариации, упорядоченные от самого медленного до самого быстрого. 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 против таблицы PostgreSQL с 300 строками:

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 требуется полное сканирование таблицы по сравнению с COUNT на optimized_random() можно использовать индекс.

Это решение я использую:

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

Это моя функция для выбора случайных строк таблицы:

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

Расширенная версия примера Лукаша, в случае, если вам нужно выбрать несколько строк наугад:

import random

# you must first select all the values of the primary key field for the table.
# in some particular cases you can use xrange(session.query(Table).count()) instead
ids = session.query(Table.primary_key_field).all() 
ids_sample = random.sample(ids, 100)

rows = session.query(Table).filter(Table.primary_key_field.in_(ids_sample))

Итак, это сообщение просто указывает на то, что вы можете использовать .in_ для выбора нескольких полей одновременно.

это решение выберет одну случайную строку

Это решение требует, чтобы первичный ключ назывался 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

есть несколько способов через 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

Oracle:

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

однако я не знаю ни одного стандартного способа