gpt4 book ai didi

python - 使用带有嵌套查询的 python MySQLDB SScursor

转载 作者:太空狗 更新时间:2023-10-29 22:26:11 30 4
gpt4 key购买 nike

当生成大量结果集时,典型的 MySQLdb 库查询会使用大量内存并且在 Python 中执行不佳。例如:

cursor.execute("SELECT id, name FROM `table`")
for i in xrange(cursor.rowcount):
id, name = cursor.fetchone()
print id, name

有一个可选的游标,一次只能获取一行,这确实加快了脚本的速度并大大减少了脚本的内存占用。

import MySQLdb
import MySQLdb.cursors

conn = MySQLdb.connect(user="user", passwd="password", db="dbname",
cursorclass = MySQLdb.cursors.SSCursor)
cur = conn.cursor()
cur.execute("SELECT id, name FROM users")
row = cur.fetchone()
while row is not None:
doSomething()
row = cur.fetchone()
cur.close()
conn.close()

但我找不到任何有关将 SSCursor 与嵌套查询一起使用的信息。如果这是 doSomething() 的定义:

def doSomething()
cur2 = conn.cursor()
cur2.execute('select id,x,y from table2')
rows = cur2.fetchall()
for row in rows:
doSomethingElse(row)
cur2.close()

然后脚本抛出以下错误:

_mysql_exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now")

听起来好像 SSCursor 与嵌套查询不兼容。真的吗?如果是这样那就太糟糕了,因为使用标准游标时主循环似乎运行得太慢。

最佳答案

MySQLdb 用户指南中的the threadsafety attribute 标题下讨论了这个问题。 (强调我的):

The MySQL protocol can not handle multiple threads using the sameconnection at once. Some earlier versions of MySQLdb utilized lockingto achieve a threadsafety of 2. While this is not terribly hard toaccomplish using the standard Cursor class (which usesmysql_store_result()), it is complicated by SSCursor (which usesmysql_use_result(); with the latter you must ensure all the rows havebeen read before another query can be executed.

MySQL C API 函数的文档 mysql_use_result()提供有关您的错误消息的更多信息:

When using mysql_use_result(), you must execute mysql_fetch_row()until a NULL value is returned, otherwise, the unfetched rows arereturned as part of the result set for your next query. The C APIgives the error Commands out of sync; you can't run this command nowif you forget to do this!

换句话说,您必须从任何无缓冲的游标(即使用 mysql_use_result() 而不是 mysql_store_result() 的游标中完全获取结果集 - 使用 MySQLdb,这意味着 SSCursorSSDictCursor),然后才能通过同一连接执行另一个语句。

在您的情况下,最直接的解决方案是打开第二个连接以在迭代无缓冲查询的结果集时使用。 (简单地从同一个连接获取缓冲游标是行不通的;在使用缓冲游标之前,您仍然必须越过未缓冲的结果集。)

如果您的工作流程类似于“遍历一个大结果集,对每一行执行 N 个小查询”,请考虑查看 MySQL 的存储过程,作为从不同连接嵌套游标的替代方法。您仍然可以使用 MySQLdb 调用过程并获得结果,尽管您肯定想要 read the documentation of MySQLdb's callproc() method因为它不符合 Python 的 database API specs检索过程输出时。


第二种选择是坚持使用缓冲游标,但将您的查询分成批处理。这就是我去年为一个项目所做的事情,我需要遍历一组数百万行,使用内部模块解析一些数据,并执行一些 INSERTUPDATE 处理每一行后查询。总体思路看起来像这样:

QUERY = r"SELECT id, name FROM `table` WHERE id BETWEEN %s and %s;"
BATCH_SIZE = 5000

i = 0
while True:
cursor.execute(QUERY, (i + 1, i + BATCH_SIZE))
result = cursor.fetchall()

# If there's no possibility of a gap as large as BATCH_SIZE in your table ids,
# you can test to break out of the loop like this (otherwise, adjust accordingly):
if not result:
break

for row in result:
doSomething()

i += BATCH_SIZE

关于您的示例代码,我要注意的另一件事是,您可以直接在 MySQLdb 中的游标上迭代,而不是在 xrange(cursor.rowcount) fetchone()。这在使用无缓冲游标时尤为重要,因为 rowcount 属性未定义并且会给出非常意外的结果(参见:Python MysqlDB using cursor.rowcount with SSDictCursor returning wrong count)。

关于python - 使用带有嵌套查询的 python MySQLDB SScursor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25772003/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com