【问题标题】:Python psycopg2 cursorsPython psycopg2 游标
【发布时间】:2015-08-06 06:59:37
【问题描述】:

来自 psycopg2 文档:

执行数据库查询时,Psycopg 游标通常会获取后端返回的所有记录,并将它们传输到客户端进程。如果查询返回的数据量很大,客户端将按比例分配大量内存。如果数据集太大而无法在客户端实际处理,则可以创建服务器端游标。

我想查询一个可能包含数千行的表,并为每一行执行一些操作。普通游标实际上会将整个数据集带到客户端吗?这听起来不太合理。代码大致如下:

conn = psycopg2.connect(url)
cursor = conn.cursor()
cursor.execute(sql)
for row in cursor:
    do some stuff
cursor.close()

我希望这是一个流式操作。第二个问题是关于游标的范围。在我的循环中,我想更新另一个表。我是否需要每次都打开一个新的光标并关闭?每个项目更新都应该在自己的事务中,因为我可能需要进行回滚。

for row in cursor:
    anotherCursor = anotherConn.cursor()
    anotherCursor.execute(update)
    if somecondition:
        anotherConn.commit()
    else:
        anotherConn.rollback
cursor.close()

======== 编辑:我对第一部分的回答 ========

好的,我将尝试回答我的问题的第一部分。普通游标实际上会在您调用执行时立即带入整个数据集,甚至在开始迭代结果集之前。您可以通过在每个步骤检查进程的内存占用来验证这一点。但是服务器端游标的需要实际上是由于 postgres 服务器而不是客户端,并记录在这里:http://www.postgresql.org/docs/9.3/static/sql-declare.html

现在,这在文档中并不是很明显,但实际上可以在事务期间临时创建此类游标。无需使用特定的 SLQ 语句等显式创建在数据库中返回 refcursor 的函数。使用 psycopg2 时,您只需在获取游标时提供名称,将为该事务创建一个临时游标。所以而不是:

 cursor = conn.cursor()

你只需要:

 cursor = conn.cursor('mycursor')

就是这样,它的工作原理。我假设在使用 JDBC 时,在设置 fetchSize 时,同样的事情也在幕后完成。它只是更透明一点。在此处查看文档:https://jdbc.postgresql.org/documentation/head/query.html#query-with-cursor

您可以通过在同一事务中查询 pg_cursors 视图来测试它是否有效。服务器端光标在获取客户端光标后出现,在关闭客户端光标后消失。所以底线:我很高兴对我的代码进行这种更改,但我必须说这对于没有 postgres 经验的人来说是一个很大的问题。

【问题讨论】:

  • 您可以通过在同一个查询中执行选择和更新来避免所有流量和光标杂项。发布真实的查询,您很可能会得到更好的答案。
  • 我猜你的意思是“更新位置”语句,对吧?在我的用例中,处理要复杂得多。
  • 我的意思是 CTE 查询。无论多么复杂。
  • 哇。这真的很简单,只需给光标一个名称以启用流式传输。谢谢。

标签: python python-2.7 psycopg2 database-cursor


【解决方案1】:

其实你已经回答了这个问题;)。

  1. 是的,您应该使用服务器端游标来获取流式传输的记录 http://initd.org/psycopg/docs/usage.html#server-side-cursors

来自文档:

CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS $$
BEGIN
    OPEN $1 FOR SELECT col FROM test;
    RETURN $1;
END;
$$ LANGUAGE plpgsql;

在代码中:

cur1 = conn.cursor()
cur1.callproc('reffunc', ['curname'])

cur2 = conn.cursor('curname')
for record in cur2:     # or cur2.fetchone, fetchmany...
    # do something with record
    pass
  1. 是的,如果您想使用服务器端光标获取行,您应该打开新光标。

【讨论】:

  • 那么使用普通游标,只要我执行 sql,整个结果集就会加载到内存中?如果我无权访问数据库来创建服务器端对象怎么办?没有类似 JDBC 结果集的东西吗?谢谢
  • 查看 JDBC 驱动程序中不需要创建显式服务器端游标的示例:jdbc.postgresql.org/documentation/head/…
  • docs:执行数据库查询时,Psycopg 游标通常会获取后端返回的所有记录,并将它们传输到客户端进程。如果查询返回的数据量很大,那么客户端将按比例分配大量内存。
  • 好的,谢谢您的回复。我想通了。实际上没有必要创建一个永久的数据库对象,看我的回答。关于我问题的第二部分,它与服务器端游标无关。只是普通的游标和事务。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多