четвер, 3 вересня 2015 р.

RollBack в цикле и ORA-1002 Fetch out of sequence

В рабочем проекте начала появляться ошибка "ORA-1002 Fetch out of sequence". Продебажил и заметил что ошибка появляется в неявном курсоре на следующей итерации. До самого курсора есть еще куча DML, после какого-то стоит Commit, после какого-то нет.
Смысл курсора такой: идет выгрузка документов в центральную базу с обновлением признака выгрузки. Вся внутренность цикла завернута в exception:
exception
  when others then
    v_st := SQLERRM;
    rollback;
    dbms_output.put_line(v_st);
end
;

После первой неудачной итерации вижу как дебагер переходит на for s in (select... и выпадает ошибка ORA-1002 Fetch out of sequence
В итоге пришлось поставить Commit перед циклом и все заработало.

Поискал в интернете и нашел статью 

Провел свои эксперименты. Первоначально создал две таблички tbl_1 и tbl_2 с одним числовым полем id1 и id2 соответственно. В Test Windows в PL/SQL Developer попытался выполнить такой код:

declare
i integer;
begin
insert into tbl_1
(id1)
values
(1);
for s in (SELECT t.* FROM all_objects t where rownum < 3) loop
begin
insert into tbl_2
(id2)
values
(1);
i := 7 / (2-2);
exception
when others then
rollback;
dbms_output.put_line(SQLERRM);
end;
end loop;
end;

И после первой итерации получаем "ORA-01002 Fetch out of sequence", а в DBMS Output только одну запись: ORA-01476: divisor is equal to zero.
Меняем код - добавляем Commit перед циклом и все отрабатывает без ошибок. На выходе 2 записи:
ORA-01476: divisor is equal to zero
ORA-01476: divisor is equal to zero

Пробуем использовать Savepoint:
/*using savepoint*/
declare
i integer;
cnt number := 0;
begin
insert into tbl_1
(id1)
values
(1);
for s in (SELECT t.* FROM all_objects t where rownum < 3) loop
cnt := cnt + 1;
execute immediate 'savepoint svp'||cnt;
begin
insert into tbl_2
(id2)
values
(1);
i := 7 / (2-2);
exception
when others then
execute immediate 'rollback to savepoint svp'||cnt;
dbms_output.put_line(SQLERRM || ' => ' || cnt);
end;
end loop;
end;

На выходе получаем 2 записи:
ORA-01476: divisor is equal to zero => 1
ORA-01476: divisor is equal to zero => 2 
 
 

  
Вольный перевод причины возникновения ошибки: Rollback возвращает нас к состоянию до открытия курсора, а значит курсор должен быть закрыт. А когда следующая итерация пробует получить данные с закрытого курсора возникает ошибка.
НО! Я пробовал посмотреть атрибут %ISOPEN, предварительно переделав все на явный курсор и НИЧЕГО. То есть атрибут после Rollback все-равно True. 
Но на будущее будем знать =)



 

Немає коментарів:

Дописати коментар