Home > other >  Use of auxiliary tables in Postgresql if temporary tables don't work
Use of auxiliary tables in Postgresql if temporary tables don't work

Time:12-05

I'm trying to implement properly a trigger function in Postgresql. The database consists of three tables, two of which are connected by a third one which is an intermediary:

Table MUSICIAN (PK = id_musician)

id_musician name birth death gender nationality num_awards
1 John Lennon 1940-10-09 1980-12-08 M British 0
2 Paul McCartney 1942-06-18 NULL M British 0
3 Joep Beving 1976-01-09 NULL M Dutch 0
4 Amy Winehouse 1983-09-14 2011-07-23 F British 0
5 Wim Mertens 1953-05-14 NULL M Belgian 0

TABLE COMPOSER (PK = FK = id_musician, id_song)

id_musician id_song
1 1
2 1
3 2
4 3
4 4
5 5

TABLE SONG (PK = id_song; FK = id_album)

id_song title duration id_album awards
1 A Hard Day's Night 00:02:34 1 1
2 Henosis 00:06:44 2 1
3 Rehab 00:03:34 3 6
4 Back To Black 00:04:01 3 2
5 Close Cover 00:03:31 4 0

The trigger function is implemented on the table SONG for calculating the column num_awards in the table MUSICIAN and the results expected are as follows:

id_musician name birth death gender nationality num_awards
1 John Lennon 1940-10-09 1980-12-08 M British 1
2 Paul McCartney 1942-06-18 NULL M British 1
3 Joep Beving 1976-01-09 NULL M Dutch 1
4 Amy Winehouse 1983-09-14 2011-07-23 F British 8
5 Wim Mertens 1953-05-14 NULL M Belgian 0

As you can see, the column num_awards sums the awards from all the songs present in the table SONG for each composer. For that purpose I've coded the following trigger function:

CREATE OR REPLACE FUNCTION update_num_awards()
RETURNS trigger AS $$ 
BEGIN
    CREATE TABLE IF NOT EXISTS bakcomp AS TABLE COMPOSER;
    CREATE TABLE IF NOT EXISTS baksong AS TABLE SONG;
    IF (TG_OP = 'INSERT') THEN 
        UPDATE MUSICIAN
        SET num_awards = num_awards   NEW.awards
        WHERE id_musician IN (SELECT c.id_musician
                            FROM COMPOSER AS c JOIN SONG
                            ON(c.id_song = NEW.id_song));
    ELSIF (TG_OP = 'UPDATE') THEN
        UPDATE MUSICIAN
        SET num_awards = num_awards   NEW.awards
        WHERE id_musician IN (SELECT c.id_musician
                            FROM COMPOSER AS c JOIN SONG
                            ON(c.id_song = NEW.id_song));
        UPDATE MUSICIAN
        SET num_awards = num_awards - OLD.awards
        WHERE id_musician IN (SELECT c.id_musician
                            FROM bakcomp AS c JOIN baksong
                            ON(c.id_song = OLD.id_song));   
    ELSIF (TG_OP = 'DELETE') THEN
        UPDATE MUSICIAN
        SET num_awards = num_awards - OLD.awards
        WHERE id_musician IN (SELECT c.id_musician
                            FROM bakcomp AS c JOIN baksong
                            ON(c.id_song = OLD.id_song));
    END IF;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE CONSTRAINT TRIGGER trigger_update_num_awards AFTER INSERT OR DELETE OR UPDATE ON SONG 
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW EXECUTE PROCEDURE update_num_awards();

The function is triggered AFTER insert, delete or update in the table SONG. When inserting, coding seems pretty straightforward but when it comes to deleting things start to get difficult - the subquery I've coded doesn't work because rows may have already disappeared from the COMPOSER and SONG tables. I've tried to use temporary tables but they don't seem to work - somehow they vanish before the delete operation begins. So the only solution I've got through with is creating two permanent auxiliary tables, bakcomp and baksong, to make a copy of COMPOSER and SONG with before each delete operation.

And my question is, how could temporary tables be performed in this case? Personally, despite my piece of code working correctly for what I want, I would like to improve it or to make it more simple and elegant.

Any help would be greatly appreciated.

Regards

CodePudding user response:

It seems like doing this on song is doing it the hard way. It's composer that has most of the effects. Delete and insert are easy. Update can be treated as a delete insert.

  • Composer
    • on delete: musician.num_awards = musician.num_awards - song.awards
    • on insert: musician.num_awards = musician.num_awards song.awards
    • on update
      • old.musician.num_awards = old.musician.num_awards - old.song.awards
      • new.musician.num_awards = new.musician.num_awards new.song.awards

When a song is inserted, it doesn't matter because it is not associated with a musician. When a song is deleted, it will cascade to delete the associated composers (assuming you've set up cascade deletes). That leaves one trigger, when a song is updated.

  • Song
    • on update: for all musicians who composed the song, musician.num_awards = musician.num_awards - old.awards new.awards

No temp tables should be necessary.

  • Related