SQLAlchemyを使用してテーブルから1つ以上のランダムな行を選択するにはどうすればよいですか?
回答:
これは非常にデータベース固有の問題です。
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では、ランダムレコードの選択には深刻なパフォーマンスの問題があることに注意してください。ここにそれについての良い記事があります。
session.query(MyModel).order_by(func.rand()).first
session.query(MyModel).order_by(func.rand()).first()
func.random()
は、データベースのランダムな実装にコンパイルされる汎用関数です。
ormを使用していて、テーブルが大きくない場合(または、行の量がキャッシュされている場合)、データベースに依存しないようにする場合は、非常に簡単な方法です。
import random
rand = random.randrange(0, session.query(Table).count())
row = session.query(Table)[rand]
これは少し不正行為ですが、それがあなたがormを使用する理由です。
random.choice(session.query(Table))
ですか?
データベースに依存しないランダムな行をプルする簡単な方法があります。.offset()を使用するだけです。すべての行をプルする必要はありません:
import random
query = DBSession.query(Table)
rowCount = int(query.count())
randomRow = query.offset(int(rowCount*random.random())).first()
Tableはテーブルです(または、そこに任意のクエリを配置できます)。いくつかの行が必要な場合は、これを複数回実行し、各行が前の行と同一でないことを確認できます。
query.offset(random.randrange(rowCount)).limit(1).first()
。
.limit(1)
以前に使用する理由はあり.first()
ますか?冗長なようです。おそらく、query.offset(random.randrange(row_count)).first()
十分です。
遅いものから速いものの順に4つのバリエーションがあります。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
300行のPostgreSQLテーブルに対してMacbookで10,000回実行した場合の結果:
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()
するの?ここで最適化された方法は何ですか?
flask-sqlalchemy
か?
一部のSQLDBMS、つまりMicrosoft SQL Server、DB2、およびPostgreSQLは、SQL:2003TABLESAMPLE
句を実装しています。バージョン1.1でSQLAlchemyにサポートが追加されました。標準は必要-それは、異なるサンプリング方法を使用して、テーブルのサンプルを戻すことを可能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で約100万行と1つの整数列を持つ単純なテーブルを使用して、(約)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
データベースからランダムな質問を選択する際に、この例の最も単純な方法を使用してください。
#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())
このソリューションでは、主キーの名前がidである必要があります。まだ指定されていない場合は、次のようにする必要があります。
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はとにかくこれらすべてを使用できると思います)
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
オラクル:
SELECT column FROM
(SELECT column FROM table
ORDER BY dbms_random.value)
WHERE rownum = 1
しかし、私は標準的な方法を知りません
select.order_by(func.random()).limit(n)