You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用psycopg2重置PostgreSQL游标?

如何使用psycopg2重置PostgreSQL游标?

我来帮你搞定这个问题!你遇到的情况是因为用了命名游标(就是创建游标时指定了name=f'cursor_{i}'),PostgreSQL的命名游标状态是保存在服务器端的——哪怕你中断脚本,如果连接没有被彻底关闭,服务器端的游标会保留上次的位置,下次重启脚本创建同名游标时就会接着之前的位置继续读。而且你的异常处理逻辑里,用了os._exit(0)直接终止进程,导致finally块里的连接关闭代码没机会执行,服务器端的游标就一直留着了。

要解决这个问题,有几个简单的方案:

方案一:改用普通游标(推荐)

普通游标是客户端维护的状态,每次重启脚本都会重新执行查询,从头开始读取数据,完全不会有残留状态的问题。修改起来很简单,只要去掉游标创建时的name参数就行:

# 把原来的命名游标改成普通游标
cur = conn.cursor()  # 去掉name参数

这样每次启动脚本,执行cur.execute()后,游标都会从结果集的开头开始,不管之前有没有中断过。

方案二:修复异常处理,确保连接和游标被彻底关闭

如果你一定要用命名游标,那得保证脚本中断时,连接和游标被正确关闭,让服务器端清理掉游标状态。你原来的代码里,except KeyboardInterrupt块里用os._exit(0)会直接终止进程,跳过finally块的执行,导致连接没关闭。可以改成这样:

import psycopg2
import pandas as pd
from datetime import datetime, date
import time
import sys
import os

for i in range(3,7):
    conn = None
    cur = None
    try:
        conn = psycopg2.connect(
            host="localhost",
            database="dbx",
            port=5432,
            user="whatever",
            options="-c search_path=dbo,data",
            password="xxxx")

        cur = conn.cursor(name=f'cursor_{i}')
        start_date = date(2023, i, 1)
        end_date = date(2023, i+1, 1) if i < 12 else date(2024, 1, 1)
        chunk_size = 100

        cur.execute("SELECT * FROM data.table WHERE datetime >= %s AND datetime <= %s", (start_date, end_date))
        rows = cur.fetchmany(chunk_size)
        while rows:
            for row in rows:
                time.sleep(0.004)
                rmq_tx= {"...some db stuff here..."}
                print(rmq_tx)
            rows = cur.fetchmany(chunk_size)

    except KeyboardInterrupt:
        print('Interrupted')
    finally:
        # 不管有没有异常,都确保关闭游标和连接
        if cur is not None:
            cur.close()
        if conn is not None:
            conn.rollback()
            conn.close()
        sys.exit(0)

这里把conn和cur的初始化放在外面,finally块里不管是否发生中断,都会关闭游标和连接,服务器端的命名游标就会被清理,下次启动脚本时创建的新游标就会从头开始。

方案三:每次启动时显式销毁同名游标(可选)

如果担心服务器端可能残留同名游标,可以在创建新游标前,先执行一条命令销毁它:

# 在创建游标前,先执行销毁命令
try:
    cur_temp = conn.cursor()
    cur_temp.execute("CLOSE cursor_%s;", (i,))
    cur_temp.close()
except psycopg2.ProgrammingError:
    # 游标不存在,忽略错误
    pass

# 然后再创建命名游标
cur = conn.cursor(name=f'cursor_{i}')

这样就能确保每次创建命名游标前,之前的同名游标已经被销毁,新游标会从头开始读取。

总结一下,最简单的就是方案一,改用普通游标,完全避免服务器端状态的问题,代码改动最小,也最可靠。

备注:内容来源于stack exchange,提问作者stanvooz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 11:13:11