Python中的cursor.fetchall()和list(cursor)有什么区别?

44

这两种方法都返回查询返回项的列表,我有没有漏掉什么,或者它们确实具有相同的用法?

在性能方面是否有任何差异?

5个回答

78
如果您使用默认的游标MySQLdb.cursors.Cursor整个结果集将在客户端(即Python列表中)被存储,直到cursor.execute()完成。
因此,即使您使用...
for row in cursor:

您不会获得任何内存占用的减少。整个结果集已经存储在列表中(请参见MySQLdb / cursors.py中的self._rows)。

但是,如果您使用SSCursor或SSDictCursor:

import MySQLdb
import MySQLdb.cursors as cursors

conn = MySQLdb.connect(..., cursorclass=cursors.SSCursor)

那么结果集将存储在服务器上,即 mysqld。现在您可以编写:

cursor = conn.cursor()
cursor.execute('SELECT * FROM HUGETABLE')
for row in cursor:
    print(row)

每次从服务器获取一行数据,不需要 Python 先构建一个大型的元组列表,因此节省内存。

否则,正如其他人已经指出的那样,cursor.fetchall()list(cursor) 本质上是相同的。


14

cursor.fetchall()list(cursor)本质上是相同的。不同之处在于不检索列表,而是直接循环遍历裸游标对象:

for result in cursor:

如果结果集很大,这种方法可以更加高效,因为它不需要获取整个结果集并将其全部保存在内存中;它只需逐个地(或按较小批次分批)逐步获取每个项目。


5
大多数PEP 249实现都符合这一点,但MySQLdb或PyMySQL不是,其中list(cursor)可以被认为比cursor.fetchall()更好(因为后者返回的类型不一致,有时是列表有时是元组,而前者始终返回一个列表),大多数游标实现在您开始迭代它们时会将整个结果集读入内存中。 - Mark Amery

6

list(cursor) 可以工作是因为游标是可迭代的。你也可以在循环中使用 cursor:

for row in cursor:
    # ...

一个好的数据库适配器实现会从服务器中以批处理的方式获取行,从而节省内存占用,因为它不需要将整个结果集保存在内存中。相反,cursor.fetchall()必须返回完整的列表。
使用list(cursor)而非cursor.fetchall()几乎毫无意义;虽然最终效果相同,但却浪费了流式传输结果的机会。

在大多数Python数据库API实现中,使用list(cursor)而不是cursor.fetchall()几乎没有意义; 结果效果确实相同,但您浪费了流结果的机会。但对于MySQLdb或其继承者PyMySQL的特定情况来说,则远非如此。在这种情况下,cursor.fetchall()具有不一致的返回类型(始终使用list(cursor)可以减少可能出现TypeError的概率),并且大多数游标子类在循环时不会进行流式传输,而是在产生第一个结果之前读取所有结果到内存中。 - Mark Amery
2
@MarkAmery:这就是为什么我小心地使用了词语“一个好的数据库适配器实现”。当我写这篇文章时,我怀疑现有的MySQL实现正在预先获取所有结果。 - Martijn Pieters

5
使用DictCursor时,(针对MySQLdb/PyMySQL)需要注意的一个区别是:list(cursor)始终会返回一个列表,而cursor.fetchall()则只有在结果集不为空的情况下才会返回一个列表,否则它会返回一个空元组。这在MySQLdb中是这样,在新的PyMySQL中仍然如此,并且由于向后兼容性的原因不会被修复。虽然这并不违反Python数据库API规范,但仍然很令人惊讶,很容易因错误地假设结果是一个列表而导致类型错误。

基于上述原因,我建议始终优先使用list(cursor)而不是cursor.fetchall(),以避免在结果集为空的边缘情况下被神秘的类型错误所困扰。


-1

您可以使用列表推导式将元组中的项转换为列表:

conn = mysql.connector.connect()
cursor = conn.cursor()
sql = "SELECT column_name FROM db.table_name;"
cursor.execute(sql)

results = cursor.fetchall()
# bring the first item of the tuple in your results here
item_0_in_result = [_[0] for _ in results]


1
这不是问题所在。而 _ 作为名称是用于表示未使用的名称的约定。在这里,您正在使用它来访问一个元素,这会造成困惑。 - BlackJack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接