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的方式遍历动态游标,具体步骤如下:
- 定义与游标返回字段类型匹配的变量;
- 循环读取游标数据,直到游标无数据可返回;
- 处理完数据后关闭游标。
修改后的完整代码:
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
相关产品推荐
相关产品推荐

