简体   繁体   中英

Enabling and disabling a trigger inside another trigger

I got a table Location

CREATE TABLE Location (
        idL     INTEGER,
        City    VARCHAR2(15) NOT NULL,
        Street  VARCHAR2(35) NOT NULL,
        Nation  CHAR(6) NOT NULL,
            
        CONSTRAINT PK_idL PRIMARY KEY(idL)
);

And a table Person

CREATE TABLE Person(
        p_Name  VARCHAR2(20) NOT NULL,
        p_Surname VARCHAR2(20) NOT NULL,
        idP     INTEGER,
        b_Date  DATE NOT NULL,
        id_PL    INTEGER,
 
        CONSTRAINT PK_idP PRIMARY KEY(idP),
        CONSTRAINT FK_idPL FOREIGN KEY(id_PL) REFERENCES Location(idL)
);

I calculate the primary key "automatically" as it follows:

CREATE SEQUENCE seq_loc_pk
start with 1
increment by 1;

CREATE OR REPLACE TRIGGER auto_pk_loc
BEFORE INSERT ON Location
FOR EACH ROW
BEGIN
    :new.idL := seq_loc_pk.nextval;
END;
/

Now I want to insert the residence for a new person (after I've created the right view of course) with an instead of trigger like this:

CREATE OR REPLACE TRIGGER newperson
INSTEAD OF INSERT ON Residence
FOR EACH ROW
DECLARE
    nl Loc.idL%TYPE;
BEGIN
    ALTER TRIGGER auto_pk_loc DISABLE; -- Error
    nl := seq_loc_pk.nextval;
    :NEW.idL := nl;
    INSERT INTO Location VALUES(:NEW.City,:NEW.Street,:NEW.Nation);
    INSERT INTO Patient VALUES(:NEW.P_Name,:NEW.P_Surname,:NEW.B_Date,,nl);
    ALTER TRIGGER auto_pk_loc ENABLE;
END;
/

I thought about disabling and enabling the trigger auto_pk_loc so that it doesn't create extra values for no reason, but I think this is not the right way to do it? What is it though? Thanks for whoever answers.

You can do this by placing it in execute immedaite :

BEGIN
    execute immedidate 'ALTER TRIGGER auto_pk_loc DISABLE'; 
    nl := seq_loc_pk.nextval;
    :NEW.idL := nl;
    INSERT INTO Location VALUES(:NEW.City,:NEW.Street,:NEW.Nation);
    INSERT INTO Patient VALUES(:NEW.P_Name,:NEW.P_Surname,:NEW.B_Date,,nl);
    execute immedidate 'ALTER TRIGGER auto_pk_loc ENABLE';
END;
/

But this will cause you all sorts of issues; DDL commits so you'll have to make this an autonomous transaction and you'll hit concurrency problems. This is best avoided.

A better method is to use the returning clause to fetch the value you just inserted:

BEGIN
    INSERT INTO Location VALUES(:NEW.City,:NEW.Street,:NEW.Nation)
      returning idl into nl;

    INSERT INTO Patient VALUES(:NEW.P_Name,:NEW.P_Surname,:NEW.B_Date,nl);
END;
/

Though as @astentx noted, you probably want to use merge to avoid having duplicate locations. This doesn't support returing , so you'll have to use some combination of insert+update instead.

Finally - assuming you're on 12c or higher - it's better to use an identity column or sequence default to auto-generate the location IDs over a trigger.

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