Посмотрев, похоже, что это нормальное поведение с MySQL. Из разных источников выглядит, что большая часть работы выбора выполняется на этапе выполнения для MySQL, и во время выборки происходит только передача сети. Я потратил так много времени на Oracle (где выполнение обычно практически ничего не имеет на практике, а мясо обработки происходит во время выборки), что я не понимал, что MySQL может вести себя по-другому.
В зависимости от контекста, обход, позволяющий выполнять итерацию без задержки через элементы, может заключаться в реализации пейджинговой системы. Это можно сделать путем инкапсуляции меньших выборок в генератор Python. С другой стороны, мы теряем согласованность данных между вызовами, но это приемлемо в моем случае. Вот основа для тех, кто заинтересован в этом подходе.Адаптация, необходимая для получения следующей страницы, делает SQL-запрос каким-то образом
сложным
беспорядочным и менее ремонтопригодным и может привязывать ваш код к вашей первичной ключевой структуре, чем вам хотелось бы, поэтому вам, вероятно, нужно взвесить плюсы и минусы для этого. Одна из хороших новостей заключается в том, что эта сложность может быть скрыта за генератором.
import MySQLdb
database = MySQLdb.connect(passwd="x", host="dbserver", user="user", db="database", port=9999)
def get_next_item(database): #Definition of this generator encapsulating the paging system
first_call = True
mysql_cursor = database.cursor()
nothing_more_found = False
while not nothing_more_found:
mysql_query = """select a, b, c, d, e, f, g from mytable use index (primary)
where a = %s order by a, b, c, d
limit 10000""" if first_call else """select a, b, c, d, e, f, g from mytable use index (primary)
where a = %s and ((b > %s) or (b = %s and c > %s) or (b = %s and c = %s and d > %s))
order by a, b, c, d
limit 10000"""
if first_call:
mysql_cursor.execute(mysql_query, ["AA", last_b, last_b, last_c, last_b, last_c, last_d])
first_call = False
else:
mysql_cursor.execute(mysql_query, ["AA"])
if mysql_cursor.rowcount == 0:
nothing_more_found = True
for a, b, c, d, e, f, g in mysql_cursor:
yield (a, b, c, d, e, f, g)
last_b, last_c, last_d = b, c, d
for a, b, c, d, e, f, g in get_next_item(database): #Usage of the generator
#Do something
Объяснение на MySQL выполнить против выборки в этом post от Майка Lischke.
Время выборки чисто измеряет, как потребовалось передать результат, , который абсолютно не связан с выполнением запроса. Время выборки может даже изменяться при каждом запуске запроса. Почему ваше сетевое подключение определяет, насколько хорошо или плохо ваш запрос? Хорошо, одно использование фактически существует: если запрос возвращает слишком много данных, передача занимает намного дольше. Но даже это не совсем так, потому что иногда результаты кэшируются, поэтому их можно отправлять быстрее.
С другой стороны, для Oracle во время выбора большая часть действий происходит во время извлечения. Это объясняется самим here
думать об этом так
1 Том Кайта) синтаксический - очень хорошо определено, что prepareStatement - мы делаем мягкий или жесткий разбор, составить заявление, выяснить как выполнить это.
2) выполнить - мы ОТКРЫВАЕМ заявление. Для обновления, для удаления, для вставка - это было бы, когда вы ОТКРЫВАЕТ инструкцию, мы выполняем ее . Вся работа здесь происходит.
для выбора более сложный. Большинство выборок будут выполнять ZERO во время выполнения выполнения. Все, что мы делаем, это открыть курсор - курсор является указателем на пространство в общем пуле, где находится план, ваши значения привязки , SCN, который представляет «время» для вашего запроса - короче курсор в этой точке - ваш контекст, ваше состояние виртуальной машины , подумайте о плане SQL, как будто это байт-код (он) выполнен как программа (она есть) на виртуальной машине (она есть). Курсор - это указатель на инструкцию (где вы находитесь в выполнении этого заявления), ваше состояние (например, регистры) и т. Д. Обычно ничего не делает здесь - он просто «готовится к рок-н-роллу, программа готова к работе, но еще не началась ».
Однако есть исключения во всем - включить трассировку и сделать выбрать * из scott.emp ДЛЯ ОБНОВЛЕНИЯ. Это выбор, но это также обновление. Вы увидите работу, выполненную во время выполнения, а также фазу выборки . Работа, выполненная во время выполнения, заключалась в том, чтобы выйти из и прикоснуться к каждой строке и заблокировать ее. Работа, выполненная во время фазы ввода , заключалась в том, что выходить и получать данные обратно к клиенту .
3) fetch - здесь мы видим почти всю работу для SELECTS (и ничего не для другого DMLS, поскольку вы не извлекаете из обновления ).
Существует два способа обработки SELECT. То, что я называю «быстрый обратный запрос» и «медленное возвращение запрос»
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:275215756923#39255764276301
отрывок из эффективного Oracle по Design описывающее это в глубине, но достаточно сказать, запрос вида:
выбрать * from one_billion_row_table;
не будет копировать данные в любом месте, не нужно будет обращаться к последней строке , прежде чем возвращать первую строку. Мы просто прочитали бы данные, как вы получите из блоков, в которых он находится.
Однако запрос вида:
выберите * от one_billion_row_table заказа по unindexed_column;
, что мы, вероятно, придется прочитать последнюю строку перед возвратом первой строки (после последней строки чтения вполне может быть первый ряд вернулся!), И мы должны были бы скопировать что-нибудь (Темп, сортировки область пространство) сначала.
В случае первого запроса, если вы:
проанализирован его (мало работы синтаксический) открыл (не реальный мир, просто получить готов) принес 1 строку и закрыл его
вы бы см. ОЧЕНЬ небольшую работу, выполненную в фазе выборки, мы должны были только прочитать один блок, возможно, чтобы вернуть первую запись.
Однако сделать те же шаги против второго запроса, и вы увидите выборку из одной строки сделать TON работы - так как мы должны найти последней строки перед первым может быть возвращены.