PL/SQL:游标使用中的 ORA-01001

Posted

技术标签:

【中文标题】PL/SQL:游标使用中的 ORA-01001【英文标题】:PL/SQL: ORA-01001 in cursor use 【发布时间】:2020-03-23 16:27:30 【问题描述】:

我正在编写一个 PL/SQL 函数来检查日期并返回一个日期。 问题是测试隔离的函数我得到了关于光标使用的错误。 ORA-01001: cursor no válido 或英文,游标无效。

我使用两个数组来保存不同类型的日期。 但是错误出现在FOR ... LOOP 循环中。先验很好地定义了游标和行变量。 而且我已经检查并且行变量包含行,查询返回至少 5 行。 这里是函数的代码:

create or replace FUNCTION OBTAIN_DATE_FIN_ABSENT
            (combination IN VARCHAR2 DEFAULT '',
            otarif1 IN VARCHAR2 DEFAULT '',
            otarif2 IN VARCHAR2 DEFAULT '',
            indicador IN NUMBER DEFAULT 0) 
           RETURN DATE 
        AS 
            i NUMBER := 1;
            solution DATE := '01/01/1970';
            previousOTARi VARCHAR2(3 CHAR) := '';

            TYPE varray_type IS VARRAY(50) OF DATE; -- Création deux arrays des dates.
            v1 varray_type;                         -- array date_begin
            v2 varray_type;                         -- array date_end

            -- Requete pour obtenir tous les lignes pour la combinaison passé.
            cursor cursorDate is            
                select cin.cin_value cin_combi, cin.date_begin date_begin, cin.date_end date_end, gin.code gcode, gin.otari_file otari_file 
                from rs2qtcin cin, rs2qtgin gin
                where cin.group_id = gin.id
                and cin.cin_value = combination
                and substr(gin.otari_file, 1, 1) = substr(otarif1, 1, 1)
                order by cin.date_begin, gin.otari_file;
            my_cursorDate cursorDate%rowtype;
        BEGIN
           --open cursorDate;
           DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Dentro!');
           FETCH cursorDate INTO my_cursorDate;
           DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Dentro1!');
           IF cursorDate%FOUND THEN         -- El cursor contiene info, sino %NOTFOUND
                DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Dentro2!');
                IF cursorDate%ROWCOUNT != 0 THEN
                    DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- cursor NO vacío!');
                ELSE
                    DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- cursor VACÍO!!!');
                END IF;
            ELSE
                DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Cursor NOTFOUND!');
            END IF;

            FOR my_cursorDate IN cursorDate LOOP
                DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Dentro3!');
                --v1(i) := my_cursorDate.date_begin;
                --v2(i) := my_cursorDate.date_end;
                DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Guardadas ambas fechas en array!.');

                IF v2(i-1) = NULL THEN
                    --solution := v1(i); -- Guardamos date_begin de la siguiente iteración

                    IF indicador = 1 and previousOTARi = otarif1 THEN
                        DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- OK!');
                    ELSE 
                        IF indicador = 2 and previousOTARi = otarif2 THEN
                            DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- ¡CHECK! this case.');
                        END IF;
                    END IF;
                END IF;
                --solution := 1;
                previousOTARi := my_cursorDate.otari_file;
                i := i+1;
           END LOOP;

           --close cursorDate;

        RETURN solution;

        EXCEPTION WHEN OTHERS THEN
            raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);    
        END;

打开和关闭游标语句已被注释,因为我正在调试代码。但是当它们被取消注释时,错误是一样的。

【问题讨论】:

哪一行导致错误? 在发布的代码中,您已注释掉打开游标语句:--open cursorDate; 。尝试取消注释以解决 ORA-01001。 【参考方案1】:

打开光标后,您需要将其关闭。

另外,我已将您的光标更改为使用现代显式连接。

我还在这部分更改了您的代码FOR rec IN cursorDate LOOP

create or replace FUNCTION OBTAIN_DATE_FIN_ABSENT
            (combination IN VARCHAR2 DEFAULT '',
             otarif1 IN VARCHAR2 DEFAULT '',
             otarif2 IN VARCHAR2 DEFAULT '',
             indicador IN NUMBER DEFAULT 0) 
RETURN DATE 
AS 

i NUMBER := 1;
solution DATE := '01/01/1970'; 
previousOTARi VARCHAR2(50) := '';

TYPE varray_type IS VARRAY(50) OF DATE; 
v1 varray_type;                
v2 varray_type;                         


cursor cursorDate is            
select cin.cin_value cin_combi
       , cin.date_begin date_begin
       , cin.date_end date_end
       , gin.code gcode
       , gin.otari_file otari_file 
from rs2qtcin cin
join rs2qtgin gin on cin.group_id = gin.id
                 and cin.cin_value = gin.combination
                 and substr(gin.otari_file, 1, 1) = substr(cin.otarif1, 1, 1)
order by cin.date_begin, gin.otari_file;

my_cursorDate cursorDate%rowtype;

BEGIN
    open cursorDate;
    FETCH cursorDate INTO my_cursorDate;

    IF cursorDate%FOUND THEN         -- El cursor contiene info, sino %NOTFOUND
        DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Dentro2!');
        IF cursorDate%ROWCOUNT != 0 THEN
            DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- cursor NO vacío!');
        ELSE
            DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- cursor VACÍO!!!');
       END IF;
    ELSE
       DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Cursor NOTFOUND!');
    END IF;
    close cursorDate;

    FOR rec IN cursorDate LOOP
       DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Dentro3!');
       DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Guardadas ambas fechas en array!.');

       IF v2(i-1) = NULL THEN
          IF indicador = 1 and previousOTARi = otarif1 THEN
             DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- OK!');
          ELSE 
             IF indicador = 2 and previousOTARi = otarif2 THEN
                DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- ¡CHECK! this case.');
             END IF;
          END IF;
       END IF;

       previousOTARi := my_cursorDate.otari_file;
       i := i+1;
    END LOOP;

    RETURN solution;

EXCEPTION WHEN OTHERS THEN
    raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);    
END;
/

Here is a demo showing the code works

【讨论】:

是的,感谢您的回答。但我不完全理解为什么我需要在 FOR..LOOP 之前关闭光标。 FOR..LOOP 结构是否也打开游标?抱歉,我把电脑留给家人了,我们在西班牙正在隔离。【参考方案2】:

您的程序存在一些结构性问题。最直接的问题是“打开和关闭游标语句被注释,因为我正在调试代码”。因为注释中的打开导致 FETCH 因游标无效而失败 - fetch 需要打开游标。您声称错误是在游标 FOR 循环上发出的。我想那是在你评论公开之前。游标 FOR 为游标发出一个打开。其中,如果游标打开,则会引发无效游标。您不能在打开的游标上使用游标 FOR。因此,您要么打开游标,处理第一行(获取),然后关闭游标,然后输入 FOR。退出循环后不要关闭光标。 嵌套 if 结构中存在逻辑问题。

IF cursorDate%FOUND THEN         -- El cursor contiene info, sino %NOTFOUND
   DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- Dentro2!');
   IF cursorDate%ROWCOUNT != 0 THEN
      DBMS_OUTPUT.PUT_LINE ('OBTAIN_DATE_FIN_ABSENT -- cursor NO vacío!');
   ELSE

“如果 cursorDate%Rowcount != 0”将始终返回 True。游标属性 %rowcount 返回从游标中提取的行数。如果最后一次提取返回一行,则属性 %Found 返回 True。由于这里 %Rowcount 是从属 %Found 意味着它永远不会返回小于 1。 另一个显而易见的是声明

IF v2(i-1) = NULL THEN 

它有两个问题。首先,你的变量 i 被初始化为 1,所以上面的语句然后读取 v2(0)。这是无效的,pl/sql 中的索引值以 1 开头。一旦正确,结果语句将永远不会为真。您不能将关系运算符与 Null 一起使用;执行时的结果始终为 Null。你需要“v2(i) is Null”。 解决这些问题,然后如果您仍然有问题,请发布另一个问题。

【讨论】:

以上是关于PL/SQL:游标使用中的 ORA-01001的主要内容,如果未能解决你的问题,请参考以下文章

循环退出条件中的“ORA-01001:无效游标”

奇怪的 PL SQL 无效游标

ORA-01001 无效游标错误

我在写游标时收到错误 ORA-01001 Invalid cursor show even number of records

过程中的PL/SQL游标问题

如何使用 pl/sql 中的游标将多列数据插入包含单列的表中?