关于mysql:为什么SQLAlchemy count()比原始查询慢得多?

Why is SQLAlchemy count() much slower than the raw query?

我正在将SQLAlchemy与MySQL数据库一起使用,我想对表中的行进行计数(大约300k)。与直接在MySQL中编写相同的查询相比,SQLAlchemy计数功能的运行时间约为50倍。我做错什么了吗?

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

但是:

1
2
3
4
5
6
7
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文档指出,可以使用query(func.count(Segment.id))

实现"优化"查询

Return a count of rows this Query would return.

This generates the SQL for this Query as follows:

1
2
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.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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的count()正在对子查询的结果进行计数,该子查询仍在进行全部工作来检索要计数的行。此行为与基础数据库无关。 MySQL没问题。

SQLAlchemy文档解释了如何通过从sqlalchemy导入func来在没有子查询的情况下发出计数。

1
2
3
4
session.query(func.count(User.id)).scalar()

>>>SELECT count(users.id) AS count_1 \
FROM users')

我花了很长时间才找到解决问题的方法。我收到以下错误:

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

更改此问题后,该问题已解决:

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

为此:

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