Reputation: 17
I am creating a sports database and am trying to add a trigger to my DDL. The aim of the trigger is to insert values into a table (commitment) whenever another table (player) has a college commitment (col_commit) change from FALSE to TRUE. Whenever this happens I want to insert the player's id and the date of the update. This is what I have so far:
CREATE OR REPLACE FUNCTION commitment_log()
RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
IF OLD.col_commit = TRUE THEN
INSERT INTO commitment(player_id, committed_date)
VALUES (OLD.player_id, now());
END IF;
RETURN NEW;
END;
$$;
CREATE TRIGGER commitment_trigger
BEFORE UPDATE
ON player
FOR EACH ROW
EXECUTE PROCEDURE commitment_log();
As of now, when I update a player's commitment in the player table to TRUE, nothing is updated in the commitment table.
Upvotes: 1
Views: 567
Reputation: 49375
OLD refers to the data that where before the update and NEW to the updated values. As you only check the OLD.col_commit data, that is FALSE, you never will get a log entry.
so simply change the trigger to check for NEW.col_commit
Belayer is right, this should be an AFTER UPDATE TRIGGER
vecause, it will only be run when the update was commited, a BEFORE UPDATE TRIGGER
will run before the commit and could change the data
CREATE OR REPLACE FUNCTION commitment_log()
RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
IF NEW.col_commit = TRUE THEN
INSERT INTO commitment(player_id, committed_date)
VALUES (OLD.player_id, now());
END IF;
RETURN NEW;
END;
$$;
CREATE TRIGGER commitment_trigger
AFTER UPDATE
ON player
FOR EACH ROW
EXECUTE PROCEDURE commitment_log();
see sample fiddle
Of course, if you want only to capture the changes from FALSE to TRUE, you need also to check the OLD value
CREATE OR REPLACE FUNCTION commitment_log()
RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
IF NEW.col_commit = TRUE AND OLD.col_commit = FALSE THEN
INSERT INTO commitment(player_id, committed_date)
VALUES (OLD.player_id, now());
END IF;
RETURN NEW;
END;
$$;
CREATE TRIGGER commitment_trigger
AFTER UPDATE
ON player
FOR EACH ROW
EXECUTE PROCEDURE commitment_log();
Upvotes: 0
Reputation: 26133
You're checking old.col_commit=TRUE
instead of new.col_commit=TRUE and old.col_commit=FALSE
that you described.
CREATE OR REPLACE FUNCTION commitment_log()
RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
INSERT INTO commitment(player_id, committed_date)
VALUES (OLD.player_id, now());
RETURN NEW;
END;
$$;
CREATE TRIGGER commitment_trigger
BEFORE UPDATE
ON player
FOR EACH ROW WHEN (new.col_commit IS True AND old.col_commit IS False)
EXECUTE PROCEDURE commitment_log();
I didn't initially stress this, but specifying your condition in trigger WHEN
section as I suggest above is just better for performance. If you do it at the beginning of trigger body, it'll always fire on update, adding overhead, even if it's not needed.
explain analyze update player set col_commit=False;--90000 rows
Update on player p (cost=0.00..13817.54 rows=0 width=0) (actual time=1910.569..1910.570 rows=0 loops=1)
-> Seq Scan on player p (cost=0.00..13817.54 rows=983554 width=7) (actual time=1.729..52.606 rows=90000 loops=1)
Planning Time: 0.086 ms
Trigger commitment_trigger: time=240.780 calls=90000
Execution Time: 1910.603 ms
If you do it in WHEN
, it'll do the check and abort without firing and incurring costs related to that. Note shorter execution time and no trigger calls compared to the earlier plan:
explain analyze update player set col_commit=False;--90000 rows
Update on player p (cost=0.00..6908.77 rows=0 width=0) (actual time=1130.119..1130.120 rows=0 loops=1)
-> Seq Scan on player p (cost=0.00..6908.77 rows=491777 width=7) (actual time=14.578..43.190 rows=90000 loops=1)
Planning Time: 0.057 ms
Execution Time: 1130.148 ms--(no trigger calls, shorter execution time)
See this demo.
Upvotes: 1