Domanda

Come seleziono una (o alcune) righe casuali da una tabella utilizzando SQLAlchemy?

È stato utile?

Soluzione

Questo è un problema specifico del database.

So che PostgreSQL, SQLite, MySQL e Oracle hanno la capacità di ordinare tramite una funzione casuale, quindi puoi usarlo in 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

Successivamente, è necessario limitare la query al numero di record necessari (ad esempio utilizzando .limit()).

Tieni presente che, almeno in PostgreSQL, la selezione di record casuali presenta gravi problemi di prestazioni; Qui è un buon articolo a riguardo.

Altri suggerimenti

Se stai usando orm e la tabella non è grande (o hai la sua quantità di righe memorizzate nella cache) e vuoi che sia indipendente dal database, l'approccio davvero semplice è.

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

Questo è leggermente un imbroglio, ma è per questo che usi un orm.

Esiste un modo semplice per estrarre una riga casuale indipendente dal database.Basta usare .offset() .Non è necessario estrarre tutte le righe:

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

Dove Table è la tua tabella (o potresti inserire qualsiasi query lì).Se vuoi alcune righe, puoi semplicemente eseguirlo più volte e assicurarti che ogni riga non sia identica alla precedente.

Ecco quattro diverse varianti, ordinate dalla più lenta alla più veloce. timeit risultati in basso:

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 risultati per 10.000 esecuzioni sul mio Macbook rispetto a una tabella PostgreSQL con 300 righe:

simple_random(): 
    90.09954111799925
load_only_random():
    65.94714171699889
order_by_random():
    23.17819356000109
optimized_random():
    19.87806927999918

Puoi facilmente vederlo usando func.random() è molto più veloce che restituire tutti i risultati a Python random.choice().

Inoltre, all'aumentare delle dimensioni della tabella, le prestazioni di order_by_random() si degraderà in modo significativo perché an ORDER BY richiede una scansione completa della tabella rispetto a COUNT In optimized_random() può utilizzare un indice.

Alcuni DBMS SQL, ovvero Microsoft SQL Server, DB2 e PostgreSQL hanno implementato SQL:2003 TABLESAMPLE clausola.È stato aggiunto il supporto a SQLAlchemy nella versione 1.1.Consente di restituire un campione di una tabella utilizzando diversi metodi di campionamento richiesti dallo standard SYSTEM E BERNOULLI, che restituiscono la percentuale approssimativa desiderata di una tabella.

In SQLAlchemy FromClause.tablesample() E tablesample() sono utilizzati per produrre a TableSample costruire:

# Approx. 1%, using SYSTEM method
sample1 = mytable.tablesample(1)

# Approx. 1%, using BERNOULLI method
sample2 = mytable.tablesample(func.bernoulli(1))

C'è un leggero problema quando viene utilizzato con le classi mappate:il prodotto TableSample l'oggetto deve avere un alias per poter essere utilizzato per interrogare gli oggetti del modello:

sample = aliased(MyModel, tablesample(MyModel, 1))
res = session.query(sample).all()

Poiché molte delle risposte contengono benchmark delle prestazioni, includerò anche qui alcuni semplici test.Utilizzando una semplice tabella in PostgreSQL con circa un milione di righe e una singola colonna intera, seleziona un campione (circa) dell'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)

Prima di affrettarsi a usarlo SYSTEM metodo di campionamento si dovrebbe sapere che campiona pagine, non tuple individuali, quindi potrebbe non essere adatto, ad esempio, per tavoli di piccole dimensioni.

Questa è la soluzione che utilizzo:

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

Questa è la mia funzione per selezionare righe casuali di una tabella:

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

questa soluzione selezionerà una singola riga casuale

Questa soluzione richiede che la chiave primaria sia denominata id, dovrebbe esserlo se non lo è già:

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

Esistono un paio di modi per utilizzare SQL, a seconda del database utilizzato.

(Penso che SQLAlchemy possa comunque utilizzare tutti questi)

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

Oracolo:

SELECT column FROM
(SELECT column FROM table
ORDER BY dbms_random.value)
WHERE rownum = 1

Tuttavia non conosco alcun modo standard

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top