الحصول على صف عشوائي من خلال SQLAlchemy
-
09-06-2019 - |
سؤال
كيف يمكنني تحديد صف (صفوف) عشوائي (أو بعض) من جدول باستخدام 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
ومع ذلك لا أعرف أي طريقة قياسية