Làm cách nào để chọn một hoặc nhiều hàng ngẫu nhiên từ bảng bằng SQLAlchemy?
Câu trả lời:
Đây là một vấn đề liên quan đến cơ sở dữ liệu.
Tôi biết rằng PostgreSQL, SQLite, MySQL và Oracle có khả năng sắp xếp theo một hàm ngẫu nhiên, vì vậy bạn có thể sử dụng điều này trong 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
Tiếp theo, bạn cần giới hạn truy vấn bởi số lượng bản ghi bạn cần (ví dụ: sử dụng .limit()
).
Hãy nhớ rằng ít nhất trong PostgreSQL, việc chọn bản ghi ngẫu nhiên có các vấn đề nghiêm trọng về hiệu suất; đây là bài báo tốt về nó.
session.query(MyModel).order_by(func.rand()).first
session.query(MyModel).order_by(func.rand()).first()
func.random()
là một hàm chung biên dịch để triển khai ngẫu nhiên của cơ sở dữ liệu.
Nếu bạn đang sử dụng orm và bảng không lớn (hoặc bạn có số lượng hàng được lưu trong bộ nhớ cache) và bạn muốn nó độc lập với cơ sở dữ liệu thì cách tiếp cận thực sự đơn giản là.
import random
rand = random.randrange(0, session.query(Table).count())
row = session.query(Table)[rand]
Điều này hơi gian lận nhưng đó là lý do tại sao bạn sử dụng orm.
random.choice(session.query(Table))
?
Có một cách đơn giản để kéo một hàng ngẫu nhiên mà cơ sở dữ liệu IS độc lập. Chỉ cần sử dụng .offset (). Không cần phải kéo tất cả các hàng:
import random
query = DBSession.query(Table)
rowCount = int(query.count())
randomRow = query.offset(int(rowCount*random.random())).first()
Nơi Table là bảng của bạn (hoặc bạn có thể đặt bất kỳ truy vấn nào ở đó). Nếu bạn muốn có một vài hàng, thì bạn có thể chạy điều này nhiều lần và đảm bảo rằng mỗi hàng không giống với hàng trước.
query.offset(random.randrange(rowCount)).limit(1).first()
.
.limit(1)
trước đây .first()
? Nó có vẻ thừa. Có lẽ, query.offset(random.randrange(row_count)).first()
là đủ.
Dưới đây là bốn biến thể khác nhau, được sắp xếp từ chậm nhất đến nhanh nhất. timeit
kết quả ở dưới cùng:
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
kết quả cho 10.000 lần chạy trên Macbook của tôi so với bảng PostgreSQL có 300 hàng:
simple_random():
90.09954111799925
load_only_random():
65.94714171699889
order_by_random():
23.17819356000109
optimized_random():
19.87806927999918
Bạn có thể dễ dàng thấy rằng việc sử dụng func.random()
nhanh hơn nhiều so với việc trả lại tất cả các kết quả cho Python random.choice()
.
Thêm vào đó, như kích thước của tăng bảng, hiệu suất của order_by_random()
sẽ làm suy giảm đáng kể vì một ORDER BY
đòi hỏi một bảng quét toàn so với COUNT
trong optimized_random()
có thể sử dụng một chỉ mục.
random.sample()
làm gì? Cách tối ưu hóa ở đây là gì?
flask-sqlalchemy
?
Một số SQL DBMS, cụ thể là Microsoft SQL Server, DB2 và PostgreSQL đã triển khai TABLESAMPLE
mệnh đề SQL: 2003 . Hỗ trợ đã được thêm vào SQLAlchemy trong phiên bản 1.1 . Nó cho phép trả về một mẫu bảng bằng các phương pháp lấy mẫu khác nhau - tiêu chuẩn yêu cầu SYSTEM
và BERNOULLI
trả về tỷ lệ phần trăm gần đúng mong muốn của một bảng.
Trong SQLAlchemy FromClause.tablesample()
và tablesample()
được sử dụng để tạo ra một TableSample
cấu trúc:
# Approx. 1%, using SYSTEM method
sample1 = mytable.tablesample(1)
# Approx. 1%, using BERNOULLI method
sample2 = mytable.tablesample(func.bernoulli(1))
Có một chút lỗi khi được sử dụng với các lớp được ánh xạ: TableSample
đối tượng được tạo ra phải có bí danh để được sử dụng để truy vấn các đối tượng mô hình:
sample = aliased(MyModel, tablesample(MyModel, 1))
res = session.query(sample).all()
Vì nhiều câu trả lời chứa điểm chuẩn hiệu suất, tôi cũng sẽ bao gồm một số bài kiểm tra đơn giản ở đây. Sử dụng một bảng đơn giản trong PostgreSQL với khoảng một triệu hàng và một cột số nguyên duy nhất, hãy chọn (ước chừng) 1% mẫu:
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)
Trước khi vội vàng sử dụng SYSTEM
phương pháp lấy mẫu, ta nên biết rằng phương pháp này lấy mẫu các trang chứ không phải các bộ dữ liệu riêng lẻ, vì vậy, ví dụ, nó có thể không phù hợp với các bảng nhỏ và có thể không tạo ra kết quả ngẫu nhiên, nếu bảng được nhóm lại.
Đây là giải pháp tôi sử dụng:
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
Đây là chức năng của tôi để chọn (các) hàng ngẫu nhiên của bảng:
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()
Sử dụng phương pháp đơn giản nhất này trong ví dụ này để chọn một câu hỏi ngẫu nhiên từ cơ sở dữ liệu: -
#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())
Giải pháp này yêu cầu khóa chính được đặt tên là id, nó phải là nếu chưa có:
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
Có một số cách thông qua SQL, tùy thuộc vào cơ sở dữ liệu nào đang được sử dụng.
(Tôi nghĩ SQLAlchemy có thể sử dụng tất cả những cách này)
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()
DB2 của IBM:
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
Tuy nhiên, tôi không biết cách tiêu chuẩn nào
select.order_by(func.random()).limit(n)