Почему ошибкa: ORA-12838: невозможно прочитать/модифицировать объект после его модификации параллельным процессом

Изучаю PL/SQL. Выдали задание, в котором нужно найти ошибки в анонимном блоке:

DECLARE
  l_Cnt_Del NUMBER;
BEGIN
  INSERT --+ append
  INTO NAME_TABLE
  SELECT Cvm.*, Trunc(SYSDATE) Calc_Date 
  FROM NAME_TABLE1 Cvm;
  DELETE FROM NAME_TABLE Cvh
  WHERE Cvh.Calc_Date < Trunc(SYSDATE) - 14;
  l_Cnt_Del := SQL%ROWCOUNT;
  COMMIT;
END;

ORA-12838: невозможно прочитать/модифицировать объект после его модификации параллельным процессом
ORA-06512: на line 8

Почему возникает эта ошибка и как ее исправить?


Запрос с таблицами на фиддле.


Ответы (1 шт):

Автор решения: 0xdb

В коде вопроса логическая ошибка. Без подсказки оптимизатору + append, код рабочий.

С подсказкой + append будет выполнена прямая вставка (Direct Path) и с ней связаны некоторые ограничения, про которые в документации сказано следующее:

You can have multiple direct-path INSERT statements in a single transaction, with or without other DML statements. However, after one DML statement alters a particular table, partition, or index, no other DML statement in the transaction can access that table, partition, or index.

После прямой вставки в таблицу нельзя обращаться к этой таблице в этой же транзакции.

Другими словами, после прямой вставки надо завершить транзакцию до того, как выполнить следуюший DML с этой таблицей (в вопросе DELETE FROM NAME_TABLE в строке 8). Посмотрите на воспроизводимом примере, как надо правильно сделать:

create table tab1 (id, name) as
    select rownum, 'name'||rownum from dual connect by level<100 
/
create table tab2 (id, name, calc_date) as
    select 1, 'name123', date'1970-01-01' from dual where 1=0
/
declare
    CntDel number;
    procedure copy is 
        pragma autonomous_transaction;
    begin
        insert /*+ append */ into tab2
        select Cvm.*, Trunc (sysdate) calc_date from tab1 Cvm;
        commit;
    end;    
begin
    copy;
    delete from tab2 Cvh
    where Cvh.calc_date < Trunc (sysdate) - 14;
    CntDel := sql%rowcount;
end;
/
PL/SQL procedure successfully completed.
→ Ссылка