I have the following table:
CREATE TABLE individuo(
codigo NUMBER(8) PRIMARY KEY,
nombre VARCHAR2(20) NOT NULL,
valor NUMBER(8) NOT NULL CHECK (valor > 0),
padre NUMBER(8) REFERENCES individuo,
nro_hijos NUMBER(8) NOT NULL CHECK (nro_hijos >=0),
CHECK(padre <> codigo)
);
Where padre means father and nro_hijos means number of children .
I need a trigger that when I insert or delete a row where padre IS NOT NULL
, it increases or decreases the nro_hijos
correspondingly.
I tried this:
CREATE OR REPLACE TRIGGER individuo_parent_increment
AFTER INSERT OR DELETE ON INDIVIDUO FOR EACH ROW WHEN (NEW.padre IS NOT NULL)
BEGIN
IF INSERTING THEN
UPDATE INDIVIDUO SET NRO_HIJOS = NRO_HIJOS + 1 WHERE CODIGO = :NEW.PADRE;
END IF;
END;
but it thew ORA-04091 (table individuo is mutating, trigger/function may not see it)
I also tried using a COMPOUND TRIGGER
but same error occured.
I have a different trigger already setup:
CREATE OR REPLACE TRIGGER individuo_initial_children
BEFORE INSERT ON INDIVIDUO
FOR EACH ROW
BEGIN
:NEW.NRO_HIJOS := 0;
END;
I ended up doing the following using a compound trigger [1] [2] :
CREATE OR REPLACE TRIGGER individuo_compound_trigger
FOR INSERT ON INDIVIDUO
COMPOUND TRIGGER
-- Create a list of number
TYPE number_arr IS TABLE OF INDIVIDUO.PADRE%TYPE
INDEX BY BINARY_INTEGER;
dads number_arr;
-- The other trigger I needed
BEFORE EACH ROW IS
BEGIN
:NEW.NRO_HIJOS := 0;
END BEFORE EACH ROW;
-- Store all affected padre.
AFTER EACH ROW IS BEGIN
IF :NEW.PADRE IS NOT NULL THEN
dads(dads.COUNT + 1) := :NEW.PADRE;
END IF;
END AFTER EACH ROW;
-- Loop through them and update them here:
AFTER STATEMENT IS BEGIN
FOR dad IN 1..dads.COUNT
LOOP
UPDATE INDIVIDUO SET NRO_HIJOS = NRO_HIJOS + 1 WHERE CODIGO = dads(dad);
END LOOP;
END AFTER STATEMENT;
END individuo_compound_trigger;
NOTE: This is an academic assignmentin which I was specifically asked to create a trigger, I do not know if this is a good solution for other purposes.
A compound trigger is the correct solution if maintaining a running total of number of children is required. A better design (imho)would be to just derive that when needed, perhaps in a view. As an assignment it is good to see you looked for and found this method. There is one slight change I would make. You should stop thinking in terms of loops . Rather than looping through the array just use FORALL resulting is a single statement to handle all the updates.
-- Update NRO_HIJOS for all dads in array
after statement is begin
forall dad in 1..dads.count
update individuo
set nro_hijos = nro_hijos + 1
where codigo = dads(dad);
end after statement;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.