Триггер ошибки: таблица мутирует, триггер/функция может этого не видеть ORA-06512:

#sql #oracle #plsql #triggers #sql-update

Вопрос:

Я написал триггер после обновления, который пересчитывает проценты для всех строк в последнем столбце всякий раз, когда меняется одно из чисел в столбце сумма. Пожалуйста, помогите мне избавиться от ошибки мутации.

ИМЯ ПЛОДА сумма PERC
Яблоки 10 нулевой
Груши 20 нулевой
Бананы 6 нулевой
Оранжевый 10 нулевой
Ананасы 4 нулевой
 create or replace trigger fruit_perc after update of AMOUNT ON FRUIT FOR EACH ROW DECLARE  TOTAL NUMBER; BEGIN  FOR i IN (SELECT PERC, AMOUNT, SUM(amount) AS TOTAL FROM FRUIT group by PERC, AMOUNT)  LOOP    i.PERC := (i.AMOUNT/i.TOTAL) * 100;  INSERT INTO FRUIT(PERC) VALUES(i.PERC);   DBMS_OUTPUT.PUT_LINE(i.amount);  END LOOP; END; /   update FRUIT set AMOUNT = 4 WHERE AMOUNT = 4; /  

Ответ №1:

Ошибка мутирующей таблицы почти всегда указывает на плохой дизайн, обычно на неработающую модель данных. Это возникает, когда у нас есть триггер, который выполняет DML в таблице, которой принадлежит триггер. Это проблема, потому что триггеры срабатывают во время транзакции: когда срабатывает триггер, состояние таблицы меняется, поэтому триггер не может получить точное состояние данных таблиц.

Рассмотрим ваше заявление об ОБНОВЛЕНИИ: если бы предложение SET было set amount = amount 1 , как триггер мог бы вычислить значение PERC для каждой строки? Является ли ОБЩАЯ сумма суммой сумм до ОБНОВЛЕНИЯ? В конце обновления? Некоторая форма скользящего итога, который накапливается по мере того, как оператор обрабатывает каждую строку? Кто может сказать? Конечно, ядро не может, и именно поэтому оно выбрасывает ORA-4091.

Предположительно, это домашнее задание. PERC является классическим примером столбца, который мы не включаем в таблицы, потому что более эффективно выводить его в SQL, когда он нам нужен, а не поддерживать его в каждой инструкции DML в таблице.

Правильный способ проецирования столбца был бы таким:

 with cte as (  select sum(amount) as tot  from fruit) select fruit.fruitname  ,fruit.amount  ,(fruit.amount/cte.tot) as perc from fruit cross join cte /  

Комментарии:

1. Чтобы перевести первый абзац (в частности, плохой дизайн) в более простую перспективу: эта ошибка существует для того, чтобы разработчик не создавал бесконечный цикл. Это в основном означает, что триггер, как правило, не может работать на столе, на котором он запускается.