MySQLデータベースでSQLAlchemyを使用していますが、テーブル内の行(約300k)をカウントしたいと思います。 SQLAlchemy count 関数は、MySQLで同じクエリを直接記述する場合の実行に約50倍の時間がかかります。私は何か間違っていますか?
_# this takes over 3 seconds to return
session.query(Segment).count()
_
しかしながら:
_SELECT COUNT(*) FROM segments;
+----------+
| COUNT(*) |
+----------+
| 281992 |
+----------+
1 row in set (0.07 sec)
_
速度の違いは、テーブルのサイズとともに大きくなります(10万行未満ではほとんど目立ちません)。
更新
session.query(Segment.id).count()
の代わりにsession.query(Segment).count()
を使用すると、トリックを実行して速度を上げることができます。しかし、なぜ最初のクエリが遅いのか、私はまだ困惑しています。
残念ながら、MySQLはサブクエリをひどくサポートしており、これは非常に否定的な形で私たちに影響を与えています。 SQLAlchemy docs は、「最適化された」クエリがquery(func.count(Segment.id))
を使用して実現できることを示しています。
このクエリが返す行の数を返します。
これにより、このクエリのSQLが次のように生成されます。
SELECT count(1) AS count_1 FROM ( SELECT <rest of query follows...> ) AS anon_1
カウントする特定の列を細かく制御するには、サブクエリの使用をスキップするか、FROM句の制御をスキップするか、他の集約関数を使用するには、query()とともにfunc式を使用します。
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)))
これを問題の解決策として見つけるのに長い時間がかかりました。次のエラーが表示されました。
sqlalchemy.exc.DatabaseError:(mysql.connector.errors.DatabaseError)126(HY000):テーブル '/tmp/#sql_40ab_0.MYI'のキーファイルが正しくありません。修理しよう
これを変更すると問題は解決しました。
query = session.query(rumorClass).filter(rumorClass.exchangeDataState == state)
return query.count()
これに:
query = session.query(func.count(rumorClass.id)).filter(rumorClass.exchangeDataState == state)
return query.scalar()
その理由は、SQLAlchemyのcount()がサブクエリの結果をカウントしているため、カウントしている行を取得するためにまだ全量の作業を行っているためです。この動作は、基礎となるデータベースにとらわれません。 MySQLの問題ではありません。
SQLAlchemy docs は、func
からsqlalchemy
をインポートして、サブクエリなしでカウントを発行する方法を説明しています。
session.query(func.count(User.id)).scalar()
>>>SELECT count(users.id) AS count_1 \nFROM users')