简体   繁体   中英

PL/SQL insert and delete trigger that updates value in same table

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM