Question

I have the following tables in my database :

CREATE TABLE tableA (   id integer NOT NULL,   ...,   PRIMARY KEY (id)
);

CREATE TABLE tableB (   id integer NOT NULL,   ...,   PRIMARY KEY (id)
);

CREATE TABLE as_for_bs (
    idA integer REFERENCES tableA(id),
    idB integer REFERENCES tableB(id),
    PRIMARY KEY (idA, idB) );

And the following triggers:

for tableA

CREATE OR REPLACE FUNCTION remove_A() RETURNS trigger AS $BODY$ BEGIN
    DELETE from as_for_bs WHERE idA = OLD.id;
    RETURN OLD; END $BODY$ LANGUAGE plpgsql;

CREATE TRIGGER remove_as_from_bs_from_A BEFORE DELETE ON tableA FOR
EACH ROW EXECUTE PROCEDURE remove_A();

for tableB

CREATE OR REPLACE FUNCTION remove_B() RETURNS trigger AS $BODY$ BEGIN
    DELETE from as_for_bs WHERE idB = OLD.id;
    RETURN OLD; END $BODY$ LANGUAGE plpgsql;

CREATE TRIGGER remove_as_for_bs_from_B BEFORE DELETE ON tableB FOR
EACH ROW EXECUTE PROCEDURE remove_B();

My problem is when I remove a row from B, the refencing rows in as_for_bs delete accordingly. But when I remove a row from A I get the error :

ERROR: update or delete on table "tableA" violates foreign key constraint "as_for_bs_fkey" on table "as_for_bs"
DETAIL: Key (id)=(999999) is still referenced from table "tableA".

I don't understand why my triggered delete works when it's on one of the field of the composite key and not the other. Is something like this supposed to work normally? Is there any reason why it wouldn't?

**on a side note: **

I know a table like as_for_bs might be a bit redundant, but I'm really just trying to test something here. There's a lot more going on while inserting values in tableA and tableB. I have integrated PostGIS in my database and everytime I add an element 'A' or 'B' I check to see if it fits into the other table. I want to precalculate all the instances where A is inside B and add them to the table so I can, hopefully, make a query looking for all the instances of 'A' inside a given id for tableB a little more quickly than using st_contains() on each row of tableA. I tried adding an array in tableA containing all the ids from tableB of which 'A' is inside, but the query looking if the id from tableB is = ANY(arrayInTableA) wasn't much faster.

EDIT: That is some ugly formatting... sorry about that, I'm not quite sure how formatting works on SO... I'll look into it soon.

Was it helpful?

Solution

What you need is ON DELETE CASCADE OR ON UPDATE CASCADE in your as_for_bs table.

So your create table script for as_for_bs should look like below

CREATE TABLE as_for_bs 
(idA integer REFERENCES tableA(id) 
ON DELETE CASCADE 
ON UPDATE CASCADE, 
idB integer REFERENCES tableB(id) 
ON DELETE CASCADE 
ON UPDATE CASCADE, 
PRIMARY KEY (idA, idB) );

Now, whenever you delete/update a row in parent/base table the same change will be cascaded/propagated to child/derived/referencing table.

OTHER TIPS

If it's just delete an entry in a referenced table, using a foreign key with ON DELETE CASCADE seems a lot easier.

Anyway, you can user triggers if you prefer and if you want to make more complex action in the future. Your code look good to me, even if I don't think it's necessary to use both a trigger and a function. You can do it all at same :

DELIMITER //
CREATE TRIGGER remove_as_from_bs_from_A BEFORE DELETE ON tableA 
FOR EACH ROW 
BEGIN 
    DELETE FROM as_for_bs 
    WHERE idA = OLD.id;
END //
CREATE TRIGGER remove_as_from_bs_from_B BEFORE DELETE ON tableB
FOR EACH ROW 
BEGIN 
    DELETE FROM as_for_bs 
    WHERE idB = OLD.id;
END //
DELIMITER ;

EDIT : my bad, I didn't see it was a PgSQL and not a MySQL database, the syntax may not be correct then.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top