15 votes

Meilleur moyen d'itérer à travers toutes les lignes d'une table DB

J'écris souvent de petits scripts Python pour itérer à travers toutes les lignes d'une table DB. Par exemple en envoyant un email à tous les abonnés.

Je le fais comme ça

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()

Je me demande s'il existe une meilleure façon de procéder car il est possible que mon code charge des milliers de lignes dans la mémoire.

J'ai pensé que ça pourrait être mieux fait avec LIMIT . Peut-être quelque chose comme ça :

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

Quelle est la meilleure façon de procéder ? Comment le feriez-vous ?

41voto

aaronasterling Points 25749

À moins que vous n'ayez des BLOBs là-dedans, des milliers de lignes ne devraient pas être un problème. Savez-vous que c'est le cas ?

Aussi, pourquoi amener la honte sur vous-même et votre famille entière en faisant quelque chose comme

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

lorsque le curseur effectuera la substitution pour vous d'une manière qui évite l'injection SQL ?

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

18voto

dugres Points 3239

Il n'est pas nécessaire de modifier la requête, vous pouvez utiliser la fonction fetchmany méthode des curseurs. Voici comment je procède :

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

De cette façon, vous pouvez "SELECT * FROM tbl_subscriber ;" mais vous ne récupérerez que un peu de à la fois.

7voto

Andrew Points 837

La plupart des connecteurs MySQL basés sur libmysqlclient mettent en mémoire tampon tous les résultats dans la mémoire du client par défaut pour des raisons de performances (en partant du principe que vous ne lirez pas de grands ensembles de résultats).

Lorsque vous devez lire un résultat volumineux dans MySQLdb, vous pouvez utiliser un SSCursor pour éviter de mettre en mémoire tampon des ensembles de résultats volumineux.

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

SSCursor - Un curseur "côté serveur". Comme Cursor mais utilise CursorUseResultMixIn. Utilisez seulement si vous avez affaire à des des ensembles de résultats potentiellement importants.

Cela introduit des complications auxquelles vous devez faire attention. Si vous ne lisez pas tous les résultats du curseur, une deuxième requête soulèvera une 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")

Cela signifie que vous devez toujours lire tout ce qui se trouve dans le curseur (et potentiellement plusieurs jeux de résultats) avant d'en émettre un autre - MySQLdb ne le fera pas pour vous.

2voto

Katalonis Points 641

Tout d'abord, vous n'avez peut-être pas besoin de Select * from...

peut-être que c'est suffisant pour toi d'avoir quelques trucs comme.. : "SELECT email from..."

qui diminuerait la quantité de mémoire utilisée de toute façon :)

1voto

Björn Pollex Points 41424

Avez-vous de réels problèmes de mémoire ? Lors de l'itération sur un curseur, les résultats sont récupérés un par un (votre implémentation DB-API peut décider de précharger les résultats, mais elle peut alors proposer une fonction pour définir le nombre de résultats préchargés).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X