Лучший способ повторить все строки в таблице DB

Я часто пишу небольшие скрипты Python для итерации по всем строкам таблицы DB. Например, отправка всем подписчикам электронной почты.

Я делаю это так:

conn = MySQLdb.connect(host = hst, user = usr, passwd = pw, db = db)
cursor = conn.cursor()
subscribers = cursor.execute("SELECT * FROM tbl_subscriber;")

for subscriber in subscribers:
 ...

conn.close()

Интересно, есть ли лучший способ сделать это, потому что возможно, что мой код загружает тысячи строк в память.

Я подумал, что с LIMIT это можно сделать лучше. Может быть, что-то вроде этого:

"SELECT * FROM tbl_subscriber LIMIT %d,%d;" % (actualLimit,steps)    

Каков наилучший способ сделать это? Как вы это сделаете?

Ответ 1

если у вас нет BLOB, тысячи строк не должны быть проблемой. Вы знаете, что это?

Кроме того, зачем приносить стыд себе и всей вашей семье, делая что-то вроде

"SELECT * FROM tbl_subscriber LIMIT %d,%d;" % (actualLimit,steps)

когда курсор сделает подстановку для вас способом, который позволяет избежать SQL-инъекции?

c.execute("SELECT * FROM tbl_subscriber LIMIT %i,%i;", (actualLimit,steps))

Ответ 2

Вам не нужно изменять запрос, вы можете использовать метод курсоров fetchmany. Вот как я это делаю:

def fetchsome(cursor, some=1000):
    fetch = cursor.fetchmany
    while True:
        rows = fetch(some)
        if not rows: break
        for row in rows:
            yield row  

Таким образом вы можете "SELECT * FROM tbl_subscriber;" но вы будете получать только некоторые за раз.

Ответ 3

Большинство соединителей MySQL, основанных на libmysqlclient, будут буферизовать все результаты в клиентской памяти по умолчанию по соображениям производительности (с предположением, что вы не будете читать большие результирующие объекты).

Когда вам нужно прочитать большой результат в MySQLdb, вы можете использовать SSCursor, чтобы избежать буферизации всего большого набора результатов.

http://mysql-python.sourceforge.net/MySQLdb.html#using-and-extending

SSCursor - Курсор "серверной стороны". Как Курсор но использует CursorUseResultMixIn. использование только если вы имеете дело с потенциально большие результирующие наборы.

Это приводит к осложнениям, которые вы должны соблюдать. Если вы не прочитаете все результаты с помощью курсора, второй запрос вызовет команду ProgrammingError:

>>> import MySQLdb
>>> import MySQLdb.cursors
>>> conn = MySQLdb.connect(read_default_file='~/.my.cnf')
>>> curs = conn.cursor(MySQLdb.cursors.SSCursor)
>>> curs.execute('SELECT * FROM big_table')
18446744073709551615L
>>> curs.fetchone()
(1L, '2c57b425f0de896fcf5b2e2f28c93f66')
>>> curs.execute('SELECT NOW()')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.6/site-packages/MySQLdb/cursors.py", line 173, in execute
    self.errorhandler(self, exc, value)
  File "/usr/lib64/python2.6/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
    raise errorclass, errorvalue
_mysql_exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now")

Это означает, что вы должны всегда читать все, начиная с курсора (и, возможно, с несколькими наборами результатов) до выдачи другого - MySQLdb не сделает этого для вас.

Ответ 4

Прежде всего, вам может не понадобиться Select * from...

Возможно, вам достаточно просто получить некоторые вещи вроде: "SELECT email from..."

что в любом случае уменьшит объем использования памяти:)

Ответ 5

У вас есть проблемы с памятью? При повторении по курсору результаты выводятся по одному (ваша реализация DB-API может принять решение о предварительной выборке результатов, но тогда она может предложить функцию для установки количества предварительно выбранных результатов).