有个疑问请各位大侠帮忙解释一下。
procedure a ,procedure b在a过程中用有如下语句
for elements in (select * from a)
loop
    b(a.xx);//b操作的表跟a表完全无关
end loop;
当这个cursor取到的第一条记录导致b过程rollback的话就会导致cursor取第二条记录的时候异常退出,报错信息是ORA-01002:fetch out of sequence.
如果b处理cursor的第一条记录正常结束的话,再遇到rollback的情况就不会报错,正常执行。

解决方案 »

  1.   

    游标打开的loop里面一般不今天遇到了一个之前从未在意过的问题引发的错误,赶紧记录一下。两个过程,此处命名为proc1,proc2,用于更新相关表中的记录。
    proc1对表记录做初始化,proc2按照业务逻辑修改表中的字段值,过程中均没有显式提交。两个过程单独执行均不会报错,但同时执行就会出错。错误号:ORA-01002: fetch out of sequence首先查看官方文档对ORA-01002错误号的解释:
    Cause: This error means that a fetch has been attempted from a cursor which is no longer valid. Note that a PL/SQL cursor loop implicitly does fetches, and thus may also cause this error. There are a number of possible causes for this error, including: 1) Fetching from a cursor after the last row has been retrieved and the ORA-1403 error returned. 2) If the cursor has been opened with the FOR UPDATE clause, fetching after a COMMIT has been issued will return the error. 3) Rebinding any placeholders in the SQL statement, then issuing a fetch before reexecuting the statement.
    Action: 1) Do not issue a fetch statement after the last row has been retrieved - there are no more rows to fetch. 2) Do not issue a COMMIT inside a fetch loop for a cursor that has been opened FOR UPDATE. 3) Reexecute the statement after rebinding, then attempt to fetch again.
    由于proc1非常简单,就是几个update语句,proc2隐式声明了cursor循环更新,逻辑比较复杂,因此初步分析问题极可能是出在proc2的逻辑处理上,重点关注proc2。
    由于pl/sql并没有提供单步调试的功能,为了清晰每步执行的状况,在相关重要位置通过dbms_output打印更新行ID等,并在适当位置加上exception捕获可能的错误。重新单独执行proc2确实捕获到了几个错误,都是:ORA-01401: inserted value too large for column根据打印输出的行号到原始表中查询了一下,确实是字段超长,重新看了看proc2的执行逻辑,由于其中对于更新操作有exception做处理,如果更新出错会继续跳到下一条,所以此处即使出错应该没关系,过程仍然会继续向下执行,这个错误应该不是造成ora-01002的主要原因。手动 rollback,然后重新同时执行proc1,proc2,这次出错,但这次只报了一次ORA-01401,接着就是ORA-01002,看起来仿佛又跟ora-01401有关系了,重点看看出错地方的代码,发现exception之后,执行了一个rollback,这个,不管是从业务逻辑,还是从程序逻辑上显然都是不合理的,去掉它之后再执行,成功!再回过头来看一看,虽然proc2中ora-1401并非造成ora-01002的主因,但如果proc2执行过程中不报错,那么自然也不会执行rollback,造成事务回滚,从而触发ora-1002错误,这也解释了为什么之前都执行的好好的,没有做任何改动的情况下忽然出现了错误。如果proc2一直正确执行的话,这个错误肯定就不会暴露出来。问题的主要原因还是因为在proc2执行出错时,执行了rollback,因此将rollback注释掉,重新编译proc2,再次执行,一切正常,问题解决!能commit或rollback的。
      

  2.   

    感谢二楼给出的解释,不过现在的问题是
    如果cursor的第一条记录使b过程正常执行的话,即使第二条记录会导致b中出现rollback,a中的loop也不会异常。
    出现异常的情况只有是在cursor取到的第一条记录就导致b过程rollback的时候。
      

  3.   

    哎我也是搜来的。很久以前有人告诉我说for update的游标在loop里面fetch的时候不要commit或update,否则会导致游标关闭。我不懂为什么的。而且你这里外面也不是for update的游标。。
      

  4.   

    如果是那样就不用请教bbs里的大侠们了,嘿
      

  5.   

    如果是那样就不用请教bbs里的大侠们了,嘿
      

  6.   

    在dml 语句处加 commit 试试。
      

  7.   


    每处理一条记录都会commit的。
      

  8.   


    代码也很简单,b过程就是使用了cursor中提供一个值。
      

  9.   

    不清楚你所说的“当这个cursor取到的第一条记录导致b过程rollback的话就会导致cursor取第二条记录的时候异常退”是怎么回事
    b过程进行insert操作? 那又是怎么进行rollback的呢?
      

  10.   


    如果cursor取到的第一条记录导致b过程rollback的话,cursor就会关闭。
    如果cursor取到的第一条记录b正常处理完毕的话,即使后面的记录导致b过程rollback,a过程中的cursor也不会关闭。b过程是使用了cursor提供的值,update和insert的其他的表。