Отримання випадкового рядка через 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.