ОРА-04091. Таблица мутирует

я хочу сделать значения двух столбцов в разных таблицах взаимоисключающими

Таблица 1 (ЖИВОТНЫЕ):

CREATE TABLE  "ANIMAL" 
(   "ANIMALID" NUMBER GENERATED BY DEFAULT AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE, 
    "NAME" VARCHAR2(255) NOT NULL ENABLE, 
    "TYPEFK" NUMBER NOT NULL ENABLE, 
    "BREEDFK" NUMBER, 
    "DATEOFBIRTH" DATE, 
    "SEX" CHAR(1) NOT NULL ENABLE, 
    "ADMITTANCEDATE" DATE NOT NULL ENABLE, 
    "AVAILABLETOFOSTER" CHAR(1) NOT NULL ENABLE, 
     CONSTRAINT "ANIMAL_PK" PRIMARY KEY ("ANIMALID")
  USING INDEX  ENABLE
   )
/ 

Таблица 2 (FOSTERAPPLICATION):

CREATE TABLE  "FOSTERAPPLICATION" 
   (    "APPLICATIONID" NUMBER GENERATED BY DEFAULT AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER  NOCYCLE  NOKEEP  NOSCALE  NOT NULL ENABLE, 
    "APPLICANTFK" NUMBER NOT NULL ENABLE, 
    "ANIMALFK" NUMBER NOT NULL ENABLE, 
    "DATEOFAPPLICATION" DATE NOT NULL ENABLE, 
    "REASONOFAPPLICATION" BLOB NOT NULL ENABLE, 
    "APPLICATIONAPPROVED" CHAR(1), 
     CONSTRAINT "FOSTERAPPLICATION_PK" PRIMARY KEY ("APPLICATIONID")
  USING INDEX  ENABLE
   )
/

у меня есть два триггера:

Триггер 1 (test_trig): 'AVAILABLETOFOSTER' = 'N', когда APPLICATIONAPPROVED = 'Y'

CREATE OR REPLACE TRIGGER test_trig
AFTER INSERT OR UPDATE ON  FOSTERAPPLICATION
FOR EACH ROW
BEGIN
UPDATE ANIMAL
SET AVAILABLETOFOSTER = 'N'
WHERE :NEW.APPLICATIONAPPROVED = 'Y'
AND :NEW.ANIMALFK = ANIMALID;
END test_trig;
/

Триггер 2 (test_trig2): APPLICATIONAPPROVED = 'N', когда AVAILABLETOFOSTER = 'Y'

CREATE OR REPLACE TRIGGER test_trig2
AFTER INSERT OR UPDATE ON ANIMAL
FOR EACH ROW
BEGIN
UPDATE FOSTERAPPLICATION
SET APPLICATIONAPPROVED = 'N'
WHERE :NEW.AVAILABLETOFOSTER = 'Y'
AND :NEW.ANIMALID = ANIMALFK;
END test_trig2;
/

концепция такова: заявка на приют животного может быть одобрена (сделано «Y») только в том случае, если животное доступно для приюта в первую очередь, и наоборот

я хочу, чтобы изменение значений запускалось автоматически, но я получаю мутирующую ошибку. я понимаю, что ошибка возникает из-за того, что я использую "UPDATE" в своем триггере... как-нибудь обойти это?


person Raphael Inebode    schedule 02.12.2020    source источник
comment
Не так просто решить в двух словах. Попробуйте прочитать эту статью stevenfeuersteinonplsql.blogspot.com/2016/12/.   -  person Jaime Drq    schedule 03.12.2020


Ответы (2)


То, что вы попытались сделать здесь, называется триггерным циклом. Рассмотрим, как это может произойти:

  1. Вы обновляете таблицу ANIMAL.
  2. Триггер test_trig_2 для таблицы ANIMAL срабатывает и обновляет FOSTERAPPLICATION.
  3. Триггер test_trig_1 в таблице FOSTERAPPLICATION срабатывает и обновляет таблицу ANIMAL.
  4. Триггер test_trig_2 в таблице ANIMAL срабатывает и обновляет FOSTERAPPLICATION.
  5. Триггер test_trig_1 в таблице FOSTERAPPLICATION срабатывает и обновляет таблицу ANIMAL.
  6. Триггер test_trig_2 в таблице ANIMAL срабатывает и обновляет FOSTERAPPLICATION.
  7. Триггер test_trig_1 в таблице FOSTERAPPLICATION срабатывает и обновляет таблицу ANIMAL.
  8. Триггер test_trig_2 в таблице ANIMAL срабатывает и обновляет FOSTERAPPLICATION.
  9. Триггер test_trig_1 в таблице FOSTERAPPLICATION срабатывает и обновляет таблицу ANIMAL.
  10. И так далее, бесконечно.

Это одна из причин, по которой Oracle не позволяет таблице, для которой был определен триггер, обновляться этим триггером, НАПРЯМУЮ ИЛИ КОСВЕННО. Здесь, например, триггер test_trig_2 для ANIMAL не пытается напрямую обновить таблицу ANIMAL, но путем обновления FOSTERAPPLICATION он косвенно пытается обновить ANIMAL при срабатывании триггера test_trig_1.

Вот почему использование какой-либо логики такого рода в триггерах — плохая идея. Вместо того, чтобы делать это, я предлагаю написать процедуру, которая выполняет все необходимые вам обновления, а затем использовать эту процедуру всякий раз, когда требуется эта функциональность.

person Bob Jarvis - Reinstate Monica    schedule 03.12.2020
comment
спасибо за отзыв, очень полезная инфа. К сожалению, я учусь на последнем курсе в области ИТ и не слишком много занимался процедурами, поэтому я даже не знаю, с чего начать (плюс я не уверен, смогу ли я достаточно хорошо изучить процедуры до истечения срока назначения). - person Raphael Inebode; 03.12.2020

Логика принятия решения о необходимости обновления содержится в вашем операторе update в качестве предложения where, на данный момент уже слишком поздно. Вместо этого вы можете просто использовать старый добрый оператор if в своем Trigger PL/SQL.

DB Fiddle с использованием демонстрационного DDL (после исправления)

person Andrew Sayer    schedule 03.12.2020
comment
Спасибо за ваш отзыв. Я не знаком с DB Fiddle, я предполагаю, что это способ тестирования и запуска кода из приложения. во-вторых, я не включил все свои столбцы в таблицы (я редактировал их). я бы использовал условие в триггере? если да, то какие условия я бы тестировал? - person Raphael Inebode; 03.12.2020
comment
Скрипка DB полезна для обмена быстрыми демонстрациями и может использовать множество различных СУБД и версий. Вы можете увидеть код, выполняемый с левой стороны, проверьте операторы создания триггеров: вместо оператора where :new.deciding_col = val я оборачиваю обновление в блок if, используя его как условие, поэтому только обновление трогается, когда значение истинно. Это означает, что когда вы обновляете tab1, а триггер обновляет tab2, триггер tab2 не запускает оператор update tab1. - person Andrew Sayer; 03.12.2020