2013-02-07 9 views
27

Sto usando SQLAlchemy con un database MySQL e mi piacerebbe contare le righe in una tabella (circa 300k). La funzione SQLAlchemy count impiega circa 50 volte più tempo per eseguire la scrittura della stessa query direttamente in MySQL. Sto facendo qualcosa di sbagliato?Perché SQLAlchemy count() è molto più lento della query non elaborata?

# this takes over 3 seconds to return 
session.query(Segment).count() 

Tuttavia:

SELECT COUNT(*) FROM segments; 
+----------+ 
| COUNT(*) | 
+----------+ 
| 281992 | 
+----------+ 
1 row in set (0.07 sec) 

La differenza di velocità aumenta con la dimensione della tabella (che è appena percettibile sotto 100k righe).

Aggiornamento

Utilizzando session.query(Segment.id).count() invece di session.query(Segment).count() sembra fare il trucco e lo fanno fino a velocità. Sono ancora perplesso sul motivo per cui la query iniziale è però più lenta.

+3

Non so che cosa SQLAlchemy, ma suona come se sarebbe iterare sul risultato invece di inviare un conteggio (*) al back-end. –

+0

I [documenti] (http://docs.sqlalchemy.org/ru/latest/orm/query.html#sqlalchemy.orm.query.Query.count) sembrano dire che la funzione di conteggio emette semplicemente un'istruzione di conteggio. – mtth

+1

Abilita la registrazione usando il parametro 'echo = True' quando crei un' engine', quindi vedi l'istruzione 'SQL' effettivamente generata. – van

risposta

42

Sfortunatamente MySQL ha un terribile e terribile supporto per le subquery e questo ci riguarda in modo molto negativo. Il SQLAlchemy docs punto che la query "ottimizzata" può essere realizzato utilizzando query(func.count(Segment.id)):

Return a count of rows this Query would return.

This generates the SQL for this Query as follows:

SELECT count(1) AS count_1 FROM (
    SELECT <rest of query follows...>) AS anon_1 

For fine grained control over specific columns to count, to skip the usage of a subquery or otherwise control of the FROM clause, or to use other aggregate functions, use func expressions in conjunction with query(), i.e.:

from sqlalchemy import func 

# count User records, without 
# using a subquery. 
session.query(func.count(User.id)) 

# return count of user "id" grouped 
# by "name" 
session.query(func.count(User.id)).\ 
     group_by(User.name) 

from sqlalchemy import distinct 

# count distinct "name" values 
session.query(func.count(distinct(User.name))) 
+0

Grazie per la risposta. Potresti approfondire il "terribile supporto delle subquery" da MySQL? – mtth

+0

la migliore spiegazione via google è sfortunatamente questo post del blog più orribilmente formattato, ma ottiene l'idea attraverso: http://www.mysqlperformanceblog.com/2010/10/25/mysql-limitations-part-3-subqueries/ – zzzeek

+0

post più lungo , ma vicino alla fine entra in maggiori dettagli sul pianificatore di MySQL a questo proposito: http: //www.xaprb.com/blog/2006/04/30/how-to-optimize-subqueries-and-joins-in-mysql/ – zzzeek

2

Mi c'è voluto molto tempo per trovare questo come la soluzione al mio problema. Stavo ottenendo il seguente errore:

sqlalchemy.exc.DatabaseError: (mysql.connector.errors.DatabaseError) 126 (HY000): Incorrect key file for table '/tmp/#sql_40ab_0.MYI'; try to repair it

Il problema è stato risolto quando ho cambiato questo:

query = session.query(rumorClass).filter(rumorClass.exchangeDataState == state) 
return query.count() 

a questo:

query = session.query(func.count(rumorClass.id)).filter(rumorClass.exchangeDataState == state) 
return query.scalar() 
0

La ragione è che il conteggio di SQLAlchemy() conta i risultati di una sottoquery che sta ancora facendo tutto il lavoro per recuperare le righe che stai contando. Questo comportamento è agnostico del database sottostante; non è un problema con MySQL.

SQLAlchemy docs spiega come emettere un conteggio senza subquery importando func da sqlalchemy.

session.query(func.count(User.id)).scalar 

>>>SELECT count(users.id) AS count_1 \nFROM users') 
Problemi correlati