Почему SQLAlchemy count () намного медленнее, чем необработанный запрос?

26

Я использую 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() похоже на трюк и получить его до скорости. Я все еще озадачен тем, почему первоначальный запрос медленнее, хотя.

  • 4
    Я не знаю, что такое SQLAlchemy, но звучит так, как будто он будет перебирать результат вместо отправки счетчика (*) на серверную часть.
  • 0
    Документы, кажется, говорят, что функция count просто выдает инструкцию count.
Показать ещё 3 комментария
Теги:
sqlalchemy

3 ответа

39
Лучший ответ

К сожалению, у 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)))
  • 0
    Спасибо за ответ. Не могли бы вы рассказать о «ужасной поддержке подзапросов» из MySQL?
  • 0
    лучшее объяснение через Google - это, к сожалению, этот ужасно отформатированный пост в блоге, но он дает представление: mysqlperformanceblog.com/2010/10/25/…
Показать ещё 5 комментариев
2

Мне потребовалось много времени, чтобы найти это как решение моей проблемы. Я получил следующую ошибку:

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()
  • 2
    Как это связано с вопросом? Если выдается ошибка, ситуация выглядит совершенно иначе, чем описано ОП
0

Причина в том, что SQLAlchemy count() подсчитывает результаты подзапроса, который все еще выполняет полный объем работы для извлечения строк, которые вы подсчитываете. Такое поведение является агностиком базовой базы данных; это не проблема с MySQL.

SQLAlchemy docs объясняет, как выдать счет без подзапроса, импортировав func из sqlalchemy.

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню