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

PostgreSQL存储过程调用报错:cursor 'bal_upd1'不存在

PostgreSQL存储过程游标报错:cursor "bal_upd1" does not exist

问题重现

我创建了一个存储过程,尝试用游标更新数据,但调用时出现错误。存储过程代码如下:

create or REPLACE PROCEDURE bal_upd(p_id int) as 
$$
    DECLARE rc record;
    ----- cursor
            bal_upd1 CURSOR (p_id int)
            for 
               select * from tbal where custid = p_id;

    begin
               open bal_upd1 (p_id);

              loop
                  FETCH bal_upd1 into rc;
                  exit when not found;
                  update  t_trans set balance = balance + rc.trans;
                  COMMIT;
             end loop;
            close bal_upd1;

    end;

$$ LANGUAGE plpgsql;

调用语句:

call bal_upd(1)

报错信息:

ERROR: cursor "bal_upd1" does not exist
CONTEXT: PL/pgSQL function bal_upd(integer) line 12 at FETCH
SQL state: 34000

错误原因

核心问题是游标参数名与存储过程入参名冲突:游标定义里的p_id int和存储过程的入参p_id int完全同名,导致PL/pgSQL解析时混淆了两者的作用域,游标无法被正确初始化打开,最终FETCH时提示游标不存在。

另外,循环内每次更新后立即执行COMMIT的做法不合理:会频繁创建事务,增加数据库日志开销,且若循环中途出错,已提交的更新无法回滚,可能造成数据不一致。

修正后的代码

create or REPLACE PROCEDURE bal_upd(p_cust_id int) as 
$$
    DECLARE 
        rc record;
        -- 修改游标参数名,避免与存储过程入参冲突
        bal_upd1 CURSOR (cursor_cust_id int)
            for select * from tbal where custid = cursor_cust_id;
    begin
        open bal_upd1 (p_cust_id);

        loop
            FETCH bal_upd1 into rc;
            exit when not found;
            update t_trans set balance = balance + rc.trans;
        end loop;
        -- 统一在循环结束后提交事务
        COMMIT;
        close bal_upd1;
    end;
$$ LANGUAGE plpgsql;

关键说明

  • 解决参数冲突:只需保证游标参数名和存储过程入参名不同即可,这里将存储过程入参改为p_cust_id,游标参数改为cursor_cust_id,让PL/pgSQL能正确区分两者的作用域,游标即可正常打开和使用。
  • 事务优化:将COMMIT移至循环外部,所有更新完成后一次性提交事务,既提升执行效率,也保证了事务的原子性——若循环中任意步骤出错,所有更新都会回滚,避免数据不一致。

内容的提问来源于stack exchange,提问作者raman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 16:40:39