ORA-04091. Таблица видоизменяется

#sql #oracle #triggers

#sql #Oracle #триггеры

Вопрос:

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

Таблица 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 (ПРИЛОЖЕНИЕ ДЛЯ СОДЕЙСТВИЯ):

 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’ при ОДОБРЕНИИ ПРИЛОЖЕНИЯ = ‘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): ПРИЛОЖЕНИЕ ОДОБРЕНО = ‘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») только в том случае, если животное доступно для усыновления в первую очередь, и наоборот

я хочу, чтобы изменение значений запускалось автоматически, но я получаю ошибку изменения. я понимаю, что ошибка возникает из-за того, что я использую «ОБНОВЛЕНИЕ» в своем триггере… есть какой-нибудь способ обойти это?

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

1. Не так просто решить в нескольких словах. Попробуйте прочитать эту статью stevenfeuersteinonplsql.blogspot.com/2016/12 /.

Ответ №1:

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

  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, но, обновляя FOSTER APPLICATION, он косвенно пытается обновить ANIMAL при test_trig_1 срабатывании триггера.

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

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

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

Ответ №2:

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

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

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

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

2. DB fiddle полезен для обмена быстрыми демонстрациями и может использовать множество разных СУБД и версий. Вы можете видеть, что код выполняется с левой стороны, ознакомьтесь с инструкциями create trigger: вместо инструкции where :new.deciding_col = val я помещаю обновление в блок if, используя его в качестве условия, поэтому обновление выполняется только тогда, когда значение равно true. Это означает, что когда вы обновляете tab1, а триггер обновляет tab2, триггер tab2 не запускает инструкцию update tab1.