skip to Main Content

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.

2

Answers


  1. 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();
    

    Basic demo

    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.

    Login or Signup to reply.
  2. 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();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search