Получение случайной строки через SQLAlchemy
Как выбрать (или некоторые) случайные строки из таблицы с помощью SQLAlchemy?
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()
намного быстрее, чем возврат всех результатов в Pythonrandom.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
однако я не знаю ни одного стандартного способа