Python psycopg2 游标

15

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

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

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

现在,从文档中并不立即显而易见,但是这种游标实际上可以在事务期间暂时创建。无需在数据库中明确创建一个返回refcursor的函数,具有特定SLQ语句等。使用psycopg2时,您只需要在获取游标时给出一个名称,然后将为该事务创建一个临时游标。因此,代替:

 cursor = conn.cursor()

你只需要:

 cursor = conn.cursor('mycursor')

就是这样,它能够起作用。我认为在使用JDBC时,在设置fetchSize时也会在幕后执行相同的操作。只是更加透明。请参见此处的文档:https://jdbc.postgresql.org/documentation/head/query.html#query-with-cursor

您可以通过在同一事务中查询pg_cursors视图来测试这是否有效。服务器端游标在获取客户端游标后出现,并且在关闭客户端游标后消失。因此,底线是:我很高兴对我的代码进行更改,但我必须说,对于那些没有太多Postgres经验的人来说,这是一个很大的坑。


你可以通过在同一个查询中执行选择和更新操作来避免所有的流量和光标烦恼。发布真实的查询,很可能会得到更好的答案。 - Clodoaldo Neto
我猜你是指使用“更新语句”,对吧?在我的用例中,处理要比那复杂得多。 - Nazaret K.
我的意思是一个CTE查询。无论其复杂性如何。 - Clodoaldo Neto
哇,只需要给光标命名就可以启用流式传输,真的很简单。谢谢。 - JohnMudd
1个回答

5

实际上,您已经回答了这个问题;)

  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 结果集这样的东西吗?谢谢。 - Nazaret K.
请参考JDBC驱动程序示例,无需创建显式的服务器端游标:https://jdbc.postgresql.org/documentation/head/query.html#query-with-cursor - Nazaret K.
文档:当执行数据库查询时,Psycopg光标通常会提取后端返回的所有记录,并将它们转移给客户端进程。如果查询返回了大量数据,则客户端将分配相应数量的内存。 - kwarunek
好的,谢谢回复。我想我已经弄清楚了。实际上,没有必要创建一个永久性的数据库对象,请看我的答案。至于我问题的第二部分,它与服务器端游标无关,只涉及普通游标和事务。 - Nazaret K.

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