SQLAlchemyを介してランダムな行を取得する


84

SQLAlchemyを使用してテーブルから1つ以上のランダムな行を選択するにはどうすればよいですか?

回答:


124

これは非常にデータベース固有の問題です。

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では、ランダムレコードの選択には深刻なパフォーマンスの問題があることに注意してください。ここにそれについての良い記事があります。


12
+1。PostgresがSQLiteで機能するのと同じ: select.order_by(func.random()).limit(n)
Mechanical_meat

Oracleではorder_by( 'dbms_random.value')を使用できます。
buttons840 2012年

11
宣言型モデルを使用している場合:session.query(MyModel).order_by(func.rand()).first
trinth 2013年

2
@trinthのおかげで、最後にparanthesisを追加したときに機能しました:session.query(MyModel).order_by(func.rand()).first()
Kent Munthe Caspersen 2015

3
SQLAlchemy v0.4以降func.random()は、データベースのランダムな実装にコンパイルされる汎用関数です。
RazerM 2016年

25

ormを使用していて、テーブルが大きくない場合(または、行の量がキャッシュされている場合)、データベースに依存しないようにする場合は、非常に簡単な方法です。

import random
rand = random.randrange(0, session.query(Table).count()) 
row = session.query(Table)[rand]

これは少し不正行為ですが、それがあなたがormを使用する理由です。


rand = random.randrange(0、session.query(Table).count())
James Brady

次のいずれかを選択する前に、すべてのオブジェクトを選択して作成します
Serge K.

どうrandom.choice(session.query(Table))ですか?
ソロモンウッコ2018

23

データベースに依存しないランダムな行をプルする簡単な方法があります。.offset()を使用するだけです。すべての行をプルする必要はありません:

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

Tableはテーブルです(または、そこに任意のクエリを配置できます)。いくつかの行が必要な場合は、これを複数回実行し、各行が前の行と同一でないことを確認できます。


更新-mysqlの約1,000万行で、これは実際には少し遅くなり始めました。最適化できると思います。
GuySoft 2013

1
〜500k行の設定でうまく機能します。
マリオ

1
現在、Oracleでは1100万行になっています....もうあまり良くありません:-)線形劣化ですが、それでも...何か他のものを見つける必要があります。
マリオ

2
@Jayme:を使用できますquery.offset(random.randrange(rowCount)).limit(1).first()
jfs 2016

1
@Jaymeも、.limit(1)以前に使用する理由はあり.first()ますか?冗長なようです。おそらく、query.offset(random.randrange(row_count)).first()十分です。
jfs 2016

17

遅いものから速いものの順に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()するの?ここで最適化された方法は何ですか?
hamidfzm 2016年

新しい質問を開いてそれにリンクすると、私は答えを突き刺します。可能であれば、答えにも影響を与えるSQLの基本的なフレーバーを指定してください。
ジェフウィドマン2016

これは使っていませんflask-sqlalchemyか?
MattSom

3

一部のSQLDBMS、つまりMicrosoft SQL Server、DB2、およびPostgreSQLは、SQL:2003TABLESAMPLE句を実装しています。バージョン1.1でSQLAlchemyサポートが追加されました。標準は必要-それは、異なるサンプリング方法を使用して、テーブルのサンプルを戻すことを可能SYSTEMBERNOULLIテーブルの所望のおおよそのパーセンテージを返しています。

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サンプリング方法を急いで使用する前に、個々のタプルではなくページをサンプリングすることを知っておく必要があります。そのため、たとえば小さなテーブルには適さない可能性があり、テーブルがクラスター化されている場合はランダムな結果として生成されない可能性があります。


0

これは私が使用するソリューションです:

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
これは大きなテーブルでは信じられないほど遅くなります。あなたはすべての行をつかんで、それをスライスするでしょう。
マシュー

1
うわー、これは素晴らしいことではありません。テーブルレコード数を取得するクエリがある場合は、それがより良いアプローチになります。これは小さなDBを備えたWebアプリで行われ、その会社とは連携しなくなったため、私はそれについて多くを行うことができません。
鶏の足

0

これは、テーブルのランダムな行を選択するための私の関数です。

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

データベースからランダムな質問を選択する際に、この例の最も単純な方法を使用してください。

#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.データベースに100万件のレコードがある場合はどうなりますか?2.それらをすべて取得して、ランダムに選択する必要がありますか?高価な電話ではないですか?
SouravBadami20年

1
絶対に高額な電話になりますが、彼は「特定の範囲のデータまたは特定のキーでランダムクエリを作成する方法」ではなく、ランダムな方法のみを要求したので、私が答えてあなたが言ったことを考慮すると、それはまったく別のトピックになります。私はできるだけ簡単に答えようとしましたので、それは明確で正確な質問のためだけです。人々はそれがより簡単である間、たくさんの線で答えます。
アナス

-2

このソリューションは、単一のランダムな行を選択します

このソリューションでは、主キーの名前が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

4
IDにギャップがある場合、これは失敗します。
erickrf 2015

-6

使用されているデータベースに応じて、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

しかし、私は標準的な方法を知りません


7
ええ。私はSQLでそれを行う方法を知っています(私はその答えをbeta.stackoverflow.com/questions/19412/…に投稿しました)が、SQLAlchemy固有のソリューションを探していました。
cnu 2008
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.