Мутация в ORACLE BD
Я делаю БД с таблицами "Товары" и "Группы товаров". Я делаю различные триггеры для этих таблиц. У меня происходит мутация, в триггере UPDATE_C_RETAIL_MARKUP. Я не совсем понимаю, как можно ее исправить. Ошибка:
One error saving changes to table "USER1"."PRODUCT_GROUP":
Row 1: ORA-04091: таблица USER1.PRODUCT_GROUP изменяется, триггер/функция может не заметить это
ORA-06512: на "USER1.UPDATE_C_RETAIL_MARKUP", line 2
ORA-04088: ошибка во время выполнения триггера 'USER1.UPDATE_C_RETAIL_MARKUP'
ORA-06512: на "USER1.UPDATE_RETAIL_PRICES", line 3
ORA-04088: ошибка во время выполнения триггера 'USER1.UPDATE_RETAIL_PRICES'
Полный код :
CREATE TABLE "USER1"."PRODUCT_GROUP"
( "ID" NUMBER(*,0) DEFAULT "USER1"."GROUP_SEQUENCE"."NEXTVAL" NOT NULL ENABLE,
"NAME" VARCHAR2(80 BYTE) NOT NULL ENABLE,
"COUNT" NUMBER(*,0) DEFAULT 0,
"C_RETAIL" NUMBER DEFAULT 0,
"MARKUP" NUMBER DEFAULT 0 NOT NULL ENABLE,
CONSTRAINT "PRODUCT_GROUP_PK" PRIMARY KEY ("ID")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ENABLE,
CONSTRAINT "PRODUCT_GROUP_CHK1" CHECK (markup >= 0 AND markup <= 1
) ENABLE
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ;
CREATE OR REPLACE NONEDITIONABLE TRIGGER "USER1"."UPDATE_RETAIL_PRICES"
AFTER UPDATE OF MARKUP ON product_group
FOR EACH ROW
BEGIN
-- Обновляем розничные цены в таблице "Товары" для соответствующей группы
UPDATE product
SET retail_price = entry_price * (1 + :NEW.markup)
WHERE group_id = :NEW.id;
END;
/
ALTER TRIGGER "USER1"."UPDATE_RETAIL_PRICES" ENABLE;
CREATE OR REPLACE NONEDITIONABLE TRIGGER "USER1"."CASCADE_DELETE_PRODUCT_GROUP"
BEFORE DELETE ON product_group
FOR EACH ROW
BEGIN
-- Удаление связанных позиций из таблицы "Товары"
DELETE FROM product WHERE group_id = :NEW.id;
END;
/
ALTER TRIGGER "USER1"."CASCADE_DELETE_PRODUCT_GROUP" ENABLE;
CREATE TABLE "USER1"."PRODUCT"
( "ID" NUMBER(*,0) DEFAULT "USER1"."PRODUCT_SEQUENCE"."NEXTVAL" NOT NULL ENABLE,
"NAME" VARCHAR2(80 BYTE) NOT NULL ENABLE,
"GROUP_ID" NUMBER(*,0) DEFAULT 0 NOT NULL ENABLE,
"ENTRY_PRICE" NUMBER DEFAULT 0,
"RETAIL_PRICE" NUMBER DEFAULT 0,
"COUNT" NUMBER(*,0) DEFAULT 0 NOT NULL ENABLE,
CONSTRAINT "PRODUCT_PK" PRIMARY KEY ("ID")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ENABLE,
CONSTRAINT "PRODUCT_FK1" FOREIGN KEY ("GROUP_ID")
REFERENCES "USER1"."PRODUCT_GROUP" ("ID") ENABLE
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ;
CREATE OR REPLACE NONEDITIONABLE TRIGGER "USER1"."DELETE_GROUP_C_RETAIL"
AFTER DELETE ON product
FOR EACH ROW
BEGIN
UPDATE product_group
SET c_retail = c_retail - (:OLD.retail_price * :OLD.count)
WHERE id = :OLD.group_id;
END;
/
ALTER TRIGGER "USER1"."DELETE_GROUP_C_RETAIL" ENABLE;
CREATE OR REPLACE NONEDITIONABLE TRIGGER "USER1"."UPDATE_RETAIL_PRICE"
BEFORE INSERT OR UPDATE OF ENTRY_PRICE ON product
FOR EACH ROW
BEGIN
DECLARE
v_markup product_group.markup%TYPE;
BEGIN
-- Получаем наценку на группу товара
SELECT markup
INTO v_markup
FROM product_group
WHERE id = :NEW.group_id;
-- Вычисляем розничную цену
:NEW.retail_price := :NEW.entry_price * (1 + v_markup);
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Если группа товара не найдена
:NEW.retail_price := 0;
END;
END;
/
ALTER TRIGGER "USER1"."UPDATE_RETAIL_PRICE" ENABLE;
CREATE OR REPLACE NONEDITIONABLE TRIGGER "USER1"."UPDATE_GROUP_COUNT"
BEFORE INSERT OR UPDATE OF COUNT ON product
FOR EACH ROW
BEGIN
-- Обновляем "COUNT" в группе товаров внутри блока транзакции
IF UPDATING THEN
UPDATE product_group SET count = count-:OLD.count+:NEW.count WHERE id = :NEW.group_id;
ELSE
UPDATE product_group SET count = count+:NEW.count WHERE id = :NEW.group_id;
END IF;
END;
/
ALTER TRIGGER "USER1"."UPDATE_GROUP_COUNT" ENABLE;
CREATE OR REPLACE NONEDITIONABLE TRIGGER "USER1"."UPDATE_GROUP_COUNT_DELETE"
AFTER DELETE ON product
FOR EACH ROW
BEGIN
-- Уменьшаем "count" в группе товаров на значение "count" удаляемого товара
UPDATE product_group
SET count = CASE
WHEN count - :OLD.count < 0 THEN 0
ELSE count - :OLD.count
END
WHERE id = :OLD.group_id;
END;
/
ALTER TRIGGER "USER1"."UPDATE_GROUP_COUNT_DELETE" ENABLE;
CREATE OR REPLACE NONEDITIONABLE TRIGGER "USER1"."INSERT_GROUP_C_RETAIL"
AFTER INSERT ON product
FOR EACH ROW
BEGIN
UPDATE product_group
SET c_retail = c_retail + (:NEW.retail_price * :NEW.count)
WHERE id = :NEW.group_id;
END;
/
ALTER TRIGGER "USER1"."INSERT_GROUP_C_RETAIL" ENABLE;
CREATE OR REPLACE NONEDITIONABLE TRIGGER "USER1"."UPDATE_GROUP_C_RETAIL"
AFTER UPDATE OF ENTRY_PRICE ON product
FOR EACH ROW
BEGIN
UPDATE product_group
SET c_retail = c_retail - (:OLD.retail_price * :NEW.count) + (:NEW.retail_price * :NEW.count)
WHERE id = :NEW.group_id;
END;
/
ALTER TRIGGER "USER1"."UPDATE_GROUP_C_RETAIL" ENABLE;
CREATE OR REPLACE NONEDITIONABLE TRIGGER "USER1"."UPDATE_C_RETAIL_MARKUP"
BEFORE UPDATE OF RETAIL_PRICE ON product
FOR EACH ROW
BEGIN
UPDATE product_group
SET c_retail = :NEW.retail_price * :NEW.count
WHERE id = :NEW.group_id;
END;
/
ALTER TRIGGER "USER1"."UPDATE_C_RETAIL_MARKUP" ENABLE;