2013-02-07 13 views
27

Ich benutze SQLAlchemy mit einer MySQL-Datenbank und ich möchte die Zeilen in einer Tabelle (etwa 300k) zählen. Die SQLAlchemy-Funktion count dauert etwa 50-mal so lange wie das Schreiben derselben Abfrage direkt in MySQL. Mache ich etwas falsch?Warum ist SQLAlchemy count() viel langsamer als die rohe Abfrage?

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

jedoch:

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

Der Unterschied in der Geschwindigkeit erhöht sich mit der Größe des Tisches (es ist kaum spürbar unter 100 K Zeilen).

aktualisieren

Mit session.query(Segment.id).count() statt session.query(Segment).count() den Trick zu tun scheint und es um Geschwindigkeit aufzustehen. Ich bin immer noch verwirrt, warum die erste Abfrage langsamer ist.

+3

Ich weiß nicht, dass SQLAlchemy Sache, aber es klingt, als würde es über das Ergebnis iterieren anstatt eine Zählung (*) an das Backend zu senden. –

+0

Die [docs] (http://docs.sqlalchemy.org/ru/latest/orm/query.html#sqlalchemy.orm.query.Query.count) scheinen zu sagen, dass die count-Funktion nur eine count-Anweisung ausgibt. – mtth

+1

Aktivieren Sie die Protokollierung mit dem Parameter echo = True, wenn Sie eine 'engine' erstellen. Sehen Sie sich dazu die tatsächlich generierte Anweisung' SQL' an. – van

Antwort

42

Leider hat MySQL schreckliche, schreckliche Unterstützung von Unterabfragen und dies wirkt sich sehr negativ auf uns aus. Die SQLAlchemy docs weist darauf hin, dass die „optimierte“ Abfrage query(func.count(Segment.id)) Verwendung erreicht werden kann:

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

Danke für die Antwort. Können Sie die "schreckliche Unterstützung von Unterabfragen" von MySQL erläutern? – mtth

+0

Die beste Erklärung über Google ist leider diese schrecklichste Blogpost, aber bekommt die Idee über: http://www.mysqlperformanceblog.com/2010/10/25/mysql-limitations-part-3-subqueries/ – zzzeek

+0

länger Post , aber am Ende geht näher auf MySQL's Planer in diesem Zusammenhang: http: //www.xaprb.com/blog/2006/04/30/how-to-optimieren-subqueries-and-joins-in-mysql/ – zzzeek

2

Es dauerte eine lange Zeit, dies als die Lösung für mein Problem zu finden. Ich war immer folgende Fehlermeldung:

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

Das Problem wurde gelöst, als ich dies geändert:

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

dazu:

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

Der Grund dafür ist, dass SQLAlchemy der count() zählt die Ergebnisse einer Unterabfrage, die immer noch die volle Menge an Arbeit erledigt, um die Zeilen, die Sie zählen, abzurufen. Dieses Verhalten ist unabhängig von der zugrunde liegenden Datenbank. Es ist kein Problem mit MySQL.

Die SQLAlchemy docs erläutert, wie Sie eine Zählung ohne Unterabfrage ausgeben, indem Sie func von sqlalchemy importieren.

Verwandte Themen