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

Oracle PL/SQL动态游标创建报错PLS-00221问题排查

问题

我想通过修改SQL语句中的表名来创建动态游标,但执行时报错:PLS-00221: C_DATOS is not a procedure or is undefined。动态语句存储在Lv_SQL变量中,调用游标C_DATOS时无法识别,请问哪里出错或遗漏了什么?

以下是我的存储过程代码:

PROCEDURE PROC_CAB_DET(Pv_corte VARCHAR2, Pv_MsjError IN OUT VARCHAR2) IS
    Lv_Table VARCHAR2(100);
    Lv_SQL   VARCHAR2(5000);
    C_DATOS  SYS_REFCURSOR;
  BEGIN
    Lv_Table := NULL;
    IF (Pv_corte IN ('02', '03')) THEN
        Lv_Table := 'TABLE_TMP_MOV';
    ELSIF (Pv_corte IN ('14', '15')) THEN
        Lv_Table := 'TABLE_TMP_FIX';
    ELSE
        Lv_Table := 'TABLE_TMP_CMF';
    END IF;

    Lv_SQL := 'SELECT cuenta, campo_2 RUBRO 
                 FROM ' || Lv_Table || ' 
                WHERE codigo = 1 
                  AND CAMPO_3 != "000" 
                  AND (campo_2 NOT IN (SELECT RUBRO FROM GSI_QC_RUBROS_CABECERA) 
                  AND upper(campo_2) NOT LIKE "NAN%") 
                MINUS 
               SELECT cuenta, campo_2 RUBRO 
                 FROM ' || Lv_Table || ' 
                WHERE codigo=4 
                  AND campo_2 != "ICE (12%)"';

    OPEN C_DATOS FOR Lv_SQL;

    FOR I IN C_DATOS LOOP      
      INSERT INTO GSI_QC_CBS_CASOS_ERROR(CUENTA, ID_ESCENARIO, DATO_TMP_1) 
      VALUES(I.CUENTA, 'IdEscenario', 'DATA');      
    END LOOP;
    COMMIT;

    CLOSE C_DATOS;
                 
    EXCEPTION
      WHEN OTHERS THEN
        Pv_MsjError := SQLERRM;
  END PROC_CAB_DET;
问题分析与解决办法

核心错误原因

FOR I IN C_DATOS LOOP是静态游标的遍历语法,不能直接用于SYS_REFCURSOR(动态游标)。PL/SQL会将C_DATOS误判为存储过程或函数调用,因此抛出PLS-00221错误。

修改方案

需改用FETCH...INTO的方式遍历动态游标,具体步骤如下:

  1. 定义与游标返回字段类型匹配的变量;
  2. 循环读取游标数据,直到游标无数据可返回;
  3. 处理完数据后关闭游标。

修改后的完整代码:

PROCEDURE PROC_CAB_DET(Pv_corte VARCHAR2, Pv_MsjError IN OUT VARCHAR2) IS
    Lv_Table VARCHAR2(100);
    Lv_SQL   VARCHAR2(5000);
    C_DATOS  SYS_REFCURSOR;
    -- 定义匹配游标返回字段的变量(类型需与原表字段一致)
    V_cuenta VARCHAR2(100);
    V_rubro  VARCHAR2(100);
  BEGIN
    Lv_Table := NULL;
    IF (Pv_corte IN ('02', '03')) THEN
        Lv_Table := 'TABLE_TMP_MOV';
    ELSIF (Pv_corte IN ('14', '15')) THEN
        Lv_Table := 'TABLE_TMP_FIX';
    ELSE
        Lv_Table := 'TABLE_TMP_CMF';
    END IF;

    -- 修正动态SQL的字符串引号:用两个单引号替代HTML转义的"
    Lv_SQL := 'SELECT cuenta, campo_2 RUBRO 
                 FROM ' || Lv_Table || ' 
                WHERE codigo = 1 
                  AND CAMPO_3 != ''000'' 
                  AND (campo_2 NOT IN (SELECT RUBRO FROM GSI_QC_RUBROS_CABECERA) 
                  AND upper(campo_2) NOT LIKE ''NAN%'') 
                MINUS 
               SELECT cuenta, campo_2 RUBRO 
                 FROM ' || Lv_Table || ' 
                WHERE codigo=4 
                  AND campo_2 != ''ICE (12%)''';

    OPEN C_DATOS FOR Lv_SQL;
    
    -- 改用FETCH遍历动态游标
    LOOP
        FETCH C_DATOS INTO V_cuenta, V_rubro;
        EXIT WHEN C_DATOS%NOTFOUND; -- 无数据时退出循环
        
        INSERT INTO GSI_QC_CBS_CASOS_ERROR(CUENTA, ID_ESCENARIO, DATO_TMP_1) 
        VALUES(V_cuenta, 'IdEscenario', 'DATA');
    END LOOP;
    
    COMMIT;
    CLOSE C_DATOS;
                 
    EXCEPTION
      WHEN OTHERS THEN
        Pv_MsjError := SQLERRM;
        -- 异常时检查游标状态,避免资源泄漏
        IF C_DATOS%ISOPEN THEN
            CLOSE C_DATOS;
        END IF;
  END PROC_CAB_DET;

额外注意事项

  • 动态SQL引号规范:PL/SQL中动态SQL的字符串需用两个单引号('')表示字符串内的单引号,原代码的"是HTML转义字符,需替换;
  • 变量类型匹配:定义的接收变量类型必须与游标返回字段完全一致,否则会触发类型转换错误;
  • 资源泄漏防范:异常分支中需检查游标是否打开,若打开则关闭,避免占用数据库资源;
  • 性能优化:若数据量较大,建议改用FORALL批量插入,替代单条插入提升执行效率。

内容的提问来源于stack exchange,提问作者Jeniffer Lorena Guillen Castro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 22:05:20