#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. Чтобы перевести первый абзац (в частности, плохой дизайн) в более простую перспективу: эта ошибка существует для того, чтобы разработчик не создавал бесконечный цикл. Это в основном означает, что триггер, как правило, не может работать на столе, на котором он запускается.