سؤال

كيف يمكنني تحديد صف (صفوف) عشوائي (أو بعض) من جدول باستخدام SQLAlchemy؟

هل كانت مفيدة؟

المحلول

هذه مشكلة خاصة بقاعدة البيانات إلى حد كبير.

أعلم أن 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.

هناك طريقة بسيطة لسحب صف عشوائي يكون قاعدة بيانات مستقلة.فقط استخدم .offset() .لا حاجة لسحب جميع الصفوف:

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

حيث Table هو الجدول الخاص بك (أو يمكنك وضع أي استعلام هناك).إذا كنت تريد بضعة صفوف، فيمكنك فقط تشغيل هذا عدة مرات، والتأكد من أن كل صف ليس مطابقًا للذي يسبقه.

إليك أربعة أشكال مختلفة، مرتبة من الأبطأ إلى الأسرع. 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() أسرع بكثير من إعادة جميع النتائج إلى بايثون random.choice().

بالإضافة إلى ذلك، كلما زاد حجم الجدول، انخفض أداء order_by_random() سوف تتحلل بشكل كبير لأن ORDER BY يتطلب مسحًا كاملاً للجدول مقابل COUNT في optimized_random() يمكن استخدام فهرس.

بعض قواعد بيانات 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 طريقة أخذ العينات ينبغي للمرء أن يعرف أنها عينات الصفحات, ، وليس صفوفًا فردية، لذلك قد لا يكون مناسبًا للطاولات الصغيرة، على سبيل المثال.

هذا هو الحل الذي أستخدمه:

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
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 يمكنه استخدام كل هذه الأشياء على أي حال)

الخلية:

SELECT colum FROM table
ORDER BY RAND()
LIMIT 1

بوستجري إس كيو إل:

SELECT column FROM table
ORDER BY RANDOM()
LIMIT 1

MSSQL:

SELECT TOP 1 column FROM table
ORDER BY NEWID()

آي بي إم DB2:

SELECT column, RAND() as IDX
FROM table
ORDER BY IDX FETCH FIRST 1 ROWS ONLY

وحي:

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

ومع ذلك لا أعرف أي طريقة قياسية

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top