Я использую SQLAlchemy с базой данных MySQL, и я хотел бы подсчитать строки в таблице (примерно 300 тыс.). Функция SQLAlchemy count занимает примерно в 50 раз больше времени для запуска в качестве написания одного и того же запроса непосредственно в MySQL. Я что-то делаю неправильно?
# this takes over 3 seconds to return
session.query(Segment).count()
Однако:
SELECT COUNT(*) FROM segments;
+----------+
| COUNT(*) |
+----------+
| 281992 |
+----------+
1 row in set (0.07 sec)
Разница в скорости увеличивается с размером таблицы (она едва заметна под 100k строк).
Обновление
Использование session.query(Segment.id).count()
вместо session.query(Segment).count()
похоже на трюк и получить его до скорости. Я все еще озадачен тем, почему первоначальный запрос медленнее, хотя.
К сожалению, у MySQL есть ужасная, страшная поддержка подзапросов, и это очень негативно влияет на нас. В документах SQLAlchemy указывается, что "оптимизированный" запрос может быть достигнут с помощью query(func.count(Segment.id))
:
Возвращает количество строк, возвращаемых этим запросом.
Это генерирует SQL для этого запроса следующим образом:
SELECT count(1) AS count_1 FROM ( SELECT <rest of query follows...> ) AS anon_1
Для мелкозернистого управления определенными столбцами для подсчета, чтобы пропустить использование подзапроса или иное управление предложением FROM или использование другие агрегированные функции, используйте выражения func в сочетании с query(), то есть:
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')