如何从PostgreSQL服务器端游标获取psycopg2的描述

5
我写了一个类,可以获取任意的Postgres SQL查询,获取数据并创建CSV文件。我使用cursor.description来获取列名,并将其作为我的CSV表头传递。然而,数据集变得太大,我正在转向服务器端游标。
服务器端游标似乎在描述中没有任何数据。当我运行以下代码时:
import psycopg2

conn = psycopg2.connect(**conn_info)
cursor = conn.cursor("server_side")
cursor.execute("select * from foo")
print(cursor.description)

它打印出None,可能是因为查询实际上没有运行。但是在这种配置下有获取列名的方法吗?

3个回答

6

cursor.execute('select...') 中的查询在服务器端执行,但应用程序尚无数据,因此 cursor.description 未定义。要获取描述,您需要从服务器端光标中至少获取一行数据,例如:

cursor = conn.cursor("server_side")
# or
# cursor = conn.cursor("server_side", scrollable=True)
# see below
cursor.execute("select * from my_table")
first_row = cursor.fetchone()
print(cursor.description)
# you can place the cursor in the initial position if needed:
# cursor.scroll(-1) 

请注意,当表为空时,您将无法获得描述。
获取命名游标的查询结果描述没有更好(更快或更简单)的方法。这是由于实现命名游标的方式所导致的。这些命令:
cursor = conn.cursor("server_side")
cursor.execute("select * from my_table")

使用Postgres命令声明游标来实现:

DECLARE "server_side" CURSOR WITHOUT HOLD FOR select * from my_table

根据文档:

DECLARE 允许用户创建游标,它可以用来从较大的查询中每次检索少量行。创建游标后,可以使用 FETCH 从中提取行。

游标声明本身不提供任何有关结果结构的信息。只有通过使用 FETCH 命令获取一行或多行后,才能获得该信息。


这个答案和这个都很有用,最好一起参考。我把我的赏金给了@Lucan,因为说实话,他们更需要这个奖励。希望你没关系。 - LondonRob

2
这里的其他答案都是不幸的,这就是原因。没有办法在不先调用fetch的情况下从服务器端游标获取description甚至rowcount。根据PEP-249,它返回None

对于不返回行或者游标还没有通过.execute*()方法调用操作的操作,该属性将为None

这是因为尽管您已经调用了execute,但服务器可能尚未执行查询,我们可以通过检查日志(当日志设置为all时)来确认这一点。
使用以下代码并等待30秒以确保清晰:
cursor = conn.cursor("server_side")
cursor.execute("select * from foo")
time.sleep(30)
cursor.fetchall()
print(cursor.description)

日志将会显示:
2020-06-19 12:11:37.687 BST [11916] LOG:  statement: BEGIN
2020-06-19 12:11:37.687 BST [11916] LOG:  statement: DECLARE "server_side" CURSOR WITHOUT HOLD FOR select * from foo
2020-06-19 12:12:07.693 BST [11916] LOG:  statement: FETCH FORWARD ALL FROM "server_side"

请注意声明和FETCH之间的30秒间隔,后者是调用,允许我们从游标中获取description

没有server_side进行比较。

2020-06-19 12:11:01.310 BST [3012] LOG:  statement: BEGIN
2020-06-19 12:11:01.311 BST [3012] LOG:  statement: select * from foo

你唯一的选项是使用“scroll”或在较大的查询之前执行“LIMIT 1”选择。
一个不太好的选择是使用“INFORMATION_SCHEMA”表,像这样:
select column_name, data_type, character_maximum_length
from INFORMATION_SCHEMA.COLUMNS where table_name = 'foo';

-1

你能不能不这样做:

import psycopg2

conn = psycopg2.connect(**conn_info)
cursor_desc = conn.cursor()
cursor_desc.execute("select * from foo limit 1")
print(cursor_desc.description)
cursor = conn.cursor("server_side")
cursor.execute("select * from foo")

那么你就不会干扰返回查询的服务器端数据。


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