Run trigger after transaction, not on each row - PostgreSQL 9.4

The trigger has to be a constraint and "after" trigger. Then you can use DEFERRABLE:

CREATE CONSTRAINT TRIGGER limit_votes
AFTER INSERT OR UPDATE ON cars
DEFERRABLE
FOR EACH ROW EXECUTE PROCEDURE limit_votes();

See the docs:

http://www.postgresql.org/docs/9.1/static/sql-createtrigger.html


According to https://www.postgresql.org/docs/13/sql-createtrigger.html

Since this should be a deferrable constraint and you want to trigger the procedure execution after the transaction you need the following,

INITIALLY DEFERRED

CREATE CONSTRAINT TRIGGER limit_votes
AFTER INSERT OR UPDATE ON cars
DEFERRABLE
INITIALLY DEFERRED 
FOR EACH ROW EXECUTE PROCEDURE limit_votes();

INITIALLY IMMEDIATE

If we have wanted to trigger the procedure execution after each statement.

CREATE CONSTRAINT TRIGGER limit_votes
AFTER INSERT OR UPDATE ON cars
DEFERRABLE
INITIALLY IMMEDIATE 
FOR EACH ROW EXECUTE PROCEDURE limit_votes();

While the existing answers take care of the run trigger after transaction part, the function is still going to run for each row. To ensure it only runs once you can add an intermediary trigger on an extra triggers table as follows:

-- triggers table for logging and firing a trigger only once
drop table if exists triggers;
create table triggers(
    "tx_id" bigint primary key,
    "name" text,
    "exec_count" int default 0
);

-- the original trigger now fires an insert into the triggers table
create constraint trigger cars_insert_update
after insert or update on cars
deferrable
initially deferred
for each row 
execute function insert_trigger();

-- trigger function to insert a row in the triggers table
create or replace function insert_trigger()
returns trigger as $insert_trigger$
begin
    insert into triggers
    values (txid_current(), tg_name, 0)
    on conflict (tx_id)
    do nothing;
    
    return null;
end
$insert_trigger$ language plpgsql;

-- trigger to run the original trigger function
-- it will only fire once since updates are ignored
drop trigger if exists triggers_insert on triggers;
create constraint trigger triggers_insert
after insert on triggers
deferrable
initially deferred
for each row
when (new."name" = 'cars_insert_update')
execute function limit_votes();

Tags:

Postgresql