92

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

4

10 に答える 10

138

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

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

于 2008-09-13T20:09:28.533 に答える
26

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

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

これは少しごまかしていますが、それがオームを使用する理由です。

于 2008-12-24T03:22:30.533 に答える
23

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

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

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

于 2013-02-16T02:19:56.087 に答える
23

以下に、最も遅いものから最も速いものへと並べた 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私の Macbook で 300 行の PostgreSQL テーブルに対して 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 BYCOUNToptimized_random()

于 2015-11-07T12:55:09.983 に答える
7

一部の SQL DBMS、つまり 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()

回答の多くにはパフォーマンス ベンチマークが含まれているため、ここにもいくつかの簡単なテストを含めます。約 100 万行と 1 つの整数列を含む 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をサンプリングすることを知っておく必要があります。そのため、たとえば小さなテーブルには適していない可能性があり、テーブルがクラスター化されている場合はランダムな結果として生成されない可能性があります。


サンプルのパーセンテージ/行数とシードをパラメーターとして渡すことを許可しないダイアレクトと、値をインライン化しないドライバーを使用する場合、値が static の場合はリテラル SQL テキストとして値を渡すか、カスタムを使用してインライン化します。 SQLA コンパイラ拡張機能:

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql import TableSample

@compiles(TableSample)
def visit_tablesample(tablesample, self, asfrom=False, **kw):
    """ Compile `TableSample` with values inlined.
    """
    kw_literal_binds = {**kw, "literal_binds": True}
    text = "%s TABLESAMPLE %s" % (
        self.visit_alias(tablesample, asfrom=True, **kw),
        tablesample._get_method()._compiler_dispatch(self, **kw_literal_binds),
    )

    if tablesample.seed is not None:
        text += " REPEATABLE (%s)" % (
            tablesample.seed._compiler_dispatch(self, **kw_literal_binds)
        )

    return text

from sqlalchemy import table, literal, text

# Static percentage
print(table("tbl").tablesample(text("5 PERCENT")))
# Compiler inlined values
print(table("tbl").tablesample(5, seed=literal(42)))
于 2018-10-07T20:00:36.487 に答える
-3

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

このソリューションでは、プライマリ キーの名前が 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
于 2014-01-20T19:18:12.990 に答える
-8

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

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

于 2008-09-13T20:04:11.103 に答える