【问题标题】:Best way to iterate through all rows in a DB-table遍历数据库表中所有行的最佳方法
【发布时间】:2010-09-24 08:14:56
【问题描述】:

我经常编写小的 Python 脚本来遍历数据库表的所有行。 例如,向所有订阅者发送电子邮件。

我是这样做的

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)    

最好的方法是什么? 你会怎么做?

【问题讨论】:

    标签: python mysql database


    【解决方案1】:

    你不必修改查询,你可以使用游标的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;”但您一次只能获取 一些。

    【讨论】:

    • 除非我弄错了,否则mysql会找到所有满足您查询的行,但在您询问之前不要发送它们。上面SELECT * FROM tbl_subscriber 的初始查询对于具有数十亿行的表会执行得非常糟糕。 LIMIT 表示 mysql 仅搜索满足您的请求的行并停止查找。 fetchmany 上的文档不是很有帮助,所以可能有一些魔力,但我对此表示怀疑。
    【解决方案2】:

    除非您在其中有 BLOB,否则数千行应该不是问题。你知道吗?

    另外,为什么要做类似

    之类的事情,让自己和整个家庭蒙羞?
    "SELECT * FROM tbl_subscriber LIMIT %d,%d;" % (actualLimit,steps)
    

    光标何时会以一种避免 SQL 注入的方式为您进行替换?

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

    【讨论】:

    • @pyfunc。我一生中写了太多的 PHP。我不能不畏惧地查看不安全的代码。
    • 感谢您的建议!我不知道 execute(...) 能够避免 SQL 注入。但是它是一个供本地使用的脚本。
    • "这是一个本地使用的脚本。"这句话说了多少次了?
    【解决方案3】:

    出于性能原因,大多数基于 libmysqlclient 的 MySQL 连接器默认将所有结果缓冲在客户端内存中(假设您不会读取大型结果集)。

    当您确实需要在 MySQLdb 中读取大型结果时,您可以使用 SSCursor 来避免缓冲整个大型结果集。

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

    SS光标 - “服务器端”游标。像光标 但使用 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...”

      这无论如何都会减少内存使用量:)

      【讨论】:

      • 好点 - SELECT * 被认为是有害的,或者至少是懒惰的编程。
      【解决方案5】:

      你有实际的记忆问题吗?遍历游标时,一次获取一个结果(您的 DB-API 实现可能决定预取结果,但随后它可能会提供一个函数来设置预取结果的数量)。

      【讨论】:

        【解决方案6】:

        RS_1st = win32com.client.Dispatch(r'ADODB.Recordset') RS_1st.Open(“SELECT colA_, colB_, colC_ FROM tbl_subscriber “, Conn, CursorType = 3, LockType = 1)

        对于 numpy.transpose(RS_1st.GetRows()).tolist() 中的 colA_、colB_、colC_:

        或

        对于 numpy.transpose(RS_1st.GetRows()).tolist() 中的 arrCol_:

        或

        cursor.execute("SELECT * FROM tbl_subscriber;")

        对于 cursor.fetchall() 中的订阅者:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-24
          • 1970-01-01
          • 2017-12-03
          • 2018-11-01
          • 2012-05-16
          • 2010-10-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多