Nhận hàng ngẫu nhiên thông qua SQLAlchemy


Câu trả lời:


122

Đâ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ó.


11
+1. Tương tự như Postgres làm việc cho SQLite: select.order_by(func.random()).limit(n)
mechanical_meat

Bạn có thể sử dụng order_by ('dbms_random.value') trong Oracle.
Buttons840

11
Nếu bạn đang sử dụng các mô hình khai báo:session.query(MyModel).order_by(func.rand()).first
thứ ba

2
Cảm ơn @trinth, nó làm việc khi tôi thêm ngoặc đến cùng:session.query(MyModel).order_by(func.rand()).first()
Kent Munthe Caspersen

3
Kể từ SQLAlchemy v0.4, 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.
RazerM

25

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.


rand = random.randrange (0, session.query (Bảng) .count ())
James Brady

Bạn chọn và tạo mọi đối tượng trước khi chọn một trong những
Serge K.

Làm thế nào về random.choice(session.query(Table))?
Solomon Ucko

23

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.


Cập nhật - với khoảng 10 triệu hàng trong mysql, điều này thực sự bắt đầu hơi chậm, tôi đoán bạn có thể tối ưu hóa nó.
GuySoft

1
Hoạt động tốt đối với tôi trong cài đặt ~ 500k hàng.
Mario

1
Bây giờ lên đến 11 triệu hàng trên Oracle .... không còn tốt nữa :-) Suy thoái tuyến tính, nhưng vẫn ... Tôi phải tìm thứ khác.
Mario,

2
@Jayme: bạn có thể sử dụng query.offset(random.randrange(rowCount)).limit(1).first().
jfs

1
@Jayme cũng vậy, có lý do để sử dụng .limit(1)trước đây .first()? Nó có vẻ thừa. Có lẽ, query.offset(random.randrange(row_count)).first()là đủ.
jfs

17

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. timeitkế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 COUNTtrong optimized_random()có thể sử dụng một chỉ mục.


Còn việc chọn mẫu? Thích random.sample()làm gì? Cách tối ưu hóa ở đây là gì?
hamidfzm

Mở một câu hỏi mới và liên kết đến nó và tôi sẽ cố gắng trả lời. Nếu có thể, hãy chỉ định hương vị cơ bản của SQL vì điều đó cũng ảnh hưởng đến câu trả lời.
Jeff Widman

Đây không phải là sử dụng flask-sqlalchemy?
MattSom

3

Một số SQL DBMS, cụ thể là Microsoft SQL Server, DB2 và PostgreSQL đã triển khai TABLESAMPLEmệ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 SYSTEMBERNOULLItrả về tỷ lệ phần trăm gần đúng mong muốn của một bảng.

Trong SQLAlchemy FromClause.tablesample()tablesample()được sử dụng để tạo ra một TableSamplecấ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 SYSTEMphươ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.


0

Đâ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

1
Điều này sẽ cực kỳ chậm trên các bảng lớn. Bạn sẽ lấy từng hàng một và sau đó cắt nó ra.
Matthew

1
Wow yeah, điều này không tuyệt vời. Nếu có một truy vấn để lấy số lượng bản ghi bảng, đó sẽ là một cách tiếp cận tốt hơn. Điều này được thực hiện trên một ứng dụng web có DB nhỏ, không còn làm việc với công ty đó nữa, vì vậy tôi không thể làm gì nhiều về nó.
ChickenFeet

0

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

-1

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

1. Điều gì sẽ xảy ra nếu có một triệu bản ghi trong cơ sở dữ liệu? 2. Chúng ta có nên lấy tất cả chúng và chọn ngẫu nhiên không? Nó sẽ không phải là một cuộc gọi đắt tiền?
Sourav Badami

1
Hoàn toàn sẽ là một cuộc gọi đắt tiền, nhưng anh ấy chỉ yêu cầu phương pháp ngẫu nhiên, không hỏi "cách thực hiện truy vấn ngẫu nhiên với một phạm vi dữ liệu cụ thể hoặc bằng một khóa cụ thể", vì vậy nếu tôi trả lời và xem xét những gì bạn đã đề cập, điều đó sẽ là chủ đề hoàn toàn khác. Tôi đã cố gắng trả lời đơn giản nhất có thể để nó sẽ rõ ràng và chỉ để điều tra chính xác. mọi người trả lời với hàng tấn dòng trong khi nó có thể đơn giản hơn.
Anas

-2

giải pháp này sẽ chọn một hàng ngẫu nhiên

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

4
Điều này không thành công khi bạn có khoảng trống trong id của mình.
erickrf

-6

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


7
Vâng. Tôi biết cách làm điều đó trong SQL (tôi đã đăng câu trả lời đó trong beta.stackoverflow.com/questions/19412/… ) nhưng đang tìm kiếm giải pháp SQLAlchemy cụ thể.
cnu 13/09/08
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.