Отримання випадкового рядка через SQLAlchemy


Відповіді:


124

Це дуже специфічна проблема бази даних.

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


12
+1. Те саме, що Postgres працює для SQLite: select.order_by(func.random()).limit(n)
mechanical_meat

Ви можете використовувати order_by ('dbms_random.value') в Oracle.
Buttons840

11
Якщо ви використовуєте декларативні моделі:session.query(MyModel).order_by(func.rand()).first
третій

2
Дякую @trinth, це спрацювало, коли я додав до кінця session.query(MyModel).order_by(func.rand()).first()
парантез

3
Оскільки SQLAlchemy v0.4, func.random()є загальною функцією, яка компілюється до випадкової реалізації бази даних.
RazerM

25

Якщо ви використовуєте orm і таблиця не велика (або у вас є кількість кешованих рядків), і ви хочете, щоб вона була незалежною від бази даних, насправді такий простий підхід.

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

Це трохи обман, але саме тому ви використовуєте орм.


rand = random.randrange (0, session.query (Table) .count ())
Джеймс Брейді,

Ви обираєте і створюєте всі об'єкти, перш ніж вибрати один із них
Сергій К.

Як щодо random.choice(session.query(Table))?
Соломон Уцко

23

Існує простий спосіб витягнути випадковий рядок, незалежний від бази даних. Просто використовуйте .offset (). Не потрібно тягнути всі рядки:

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

Де таблиця - це ваша таблиця (або ви можете туди поставити будь-який запит). Якщо вам потрібно кілька рядків, ви можете просто запустити це кілька разів і переконатися, що кожен рядок не ідентичний попередньому.


Оновлення - приблизно в 10 мільйонів рядків у mysql це насправді почало ставати трохи повільним, я думаю, ви могли б його оптимізувати.
GuySoft

1
Для мене добре працює в налаштуваннях ~ 500 тис. Рядків.
Маріо

1
Зараз на Oracle 11 мільйонів рядків .... вже не так добре :-) Лінійна деградація, але все ж ... Мені потрібно знайти щось інше.
Маріо

2
@Jayme: ти міг би використати query.offset(random.randrange(rowCount)).limit(1).first().
jfs

1
@Jayme також, чи є причина використовувати .limit(1)раніше .first()? Це здається зайвим. Можливо, query.offset(random.randrange(row_count)).first()досить.
jfs

17

Ось чотири різні варіанти, впорядковані від найповільніших до найшвидших. 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 результати для 10000 запусків на моєму 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()може використовувати індекс.


А як щодо відбору зразків? Як що random.sample()робити? Що тут оптимізовано?
hamidfzm

Відкрийте нове запитання та надішліть на нього посилання, і я відповім відповідь. Якщо можливо, вкажіть основний смак SQL, оскільки це також впливає на відповідь.
Джефф Відман,

Хіба це не використовується flask-sqlalchemy?
MattSom

3

Деякі СУБД SQL, а саме Microsoft SQL Server, DB2 та PostgreSQL , реалізували пропозицію SQL: 2003 TABLESAMPLE. Підтримка була додана до SQLAlchemy у версії 1.1 . Це дозволяє повернути зразок таблиці, використовуючи різні методи вибірки - стандарт вимагає SYSTEMі BERNOULLI, які повертають бажаний приблизний відсоток таблиці.

У SQLAlchemy FromClause.tablesample()і tablesample()використовуються для створення TableSampleконструкції:

# Approx. 1%, using SYSTEM method
sample1 = mytable.tablesample(1)

# Approx. 1%, using BERNOULLI method
sample2 = mytable.tablesample(func.bernoulli(1))

Існує невелика помилка при використанні з зіставленими класами: створений TableSampleоб'єкт повинен бути псевдонімом, щоб використовувати його для запиту об'єктів моделі:

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

Оскільки багато відповідей містять показники ефективності, я також включу сюди кілька простих тестів. Використовуючи просту таблицю в PostgreSQL з приблизно мільйоном рядків та єдиним цілим стовпцем, виберіть (приблизно) 1% вибірки:

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)

Перш ніж поспішати використовувати SYSTEMметод вибірки, слід знати, що він відбирає сторінки , а не окремі кортежі, тому, можливо, він не підходить для невеликих таблиць, наприклад, і може не дати випадкових результатів, якщо таблиця кластеризована.


0

Це рішення, яке я використовую:

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

1
На великих столах це було б неймовірно повільно. Ви б захопили кожен рядок, а потім нарізали його.
Метью

1
Ого, так, це не чудово. Якщо є запит, щоб отримати кількість записів таблиці, це був би кращий підхід. Це було зроблено у веб-додатку з невеликою БД, який більше не працює з цією компанією, тому я не можу багато з цим зробити.
ChickenFeet

0

Це моя функція для вибору випадкових рядків таблиці:

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

-1

Використовуйте цей найпростіший метод у цьому прикладі для вибору випадкового запитання з бази даних: -

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

1. Що робити, якщо в базі даних є мільйон записів? 2. Чи варто дістати їх усіх і вибрати випадковий? Це не буде дорогим дзвінком?
Sourav Badami

1
Безумовно, це буде дорогий дзвінок, але він попросив лише випадковий метод, не запитуючи "як зробити випадковий запит з певним діапазоном даних або за певним ключем", тому, якщо я відповім і врахую те, що ви згадали, це буде бути абсолютно іншою темою. Я намагався відповісти якомога простіше, щоб це було зрозуміло і лише для точного запитання. люди відповідають тоннами рядків, хоча це може бути простіше.
Анас

-2

це рішення вибере один випадковий рядок

Це рішення вимагає, щоб первинний ключ називався ідентифікатором, він повинен бути, якщо його ще немає:

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

4
Це не вдається, коли у вас є пробіл у вашому ідентифікаторі.
erickrf

-6

Існує кілька способів використання 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

Однак я не знаю жодного стандартного способу


7
Ага. Я знаю, як це зробити в SQL (цю відповідь я розмістив у beta.stackoverflow.com/questions/19412/… ), але шукав рішення, специфічне для SQLAlchemy.
cnu
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.