PostgreSQL Triggers: Automatically Running Code on INSERT, UPDATE, DELETE
Learn how PostgreSQL triggers work — CREATE TRIGGER paired with a trigger function, BEFORE vs AFTER timing, and a real example that auto-updates an updated_at column.
PostgreSQL Triggers
A trigger automatically runs a function whenever a specified event (INSERT, UPDATE, or DELETE) happens on a table — useful for auditing, validation, and keeping derived data in sync without relying on application code to remember.
Step 1: Write a Trigger Function
Trigger functions are special — they return TRIGGER, and reference the affected row through NEW (the incoming row) and OLD (the previous row).
CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Step 2: Attach the Trigger to a Table
CREATE TRIGGER trg_set_updated_at
BEFORE UPDATE ON employees
FOR EACH ROW
EXECUTE FUNCTION set_updated_at();
Now every UPDATE on employees automatically stamps updated_at — no application code needs to remember to set it.
UPDATE employees SET salary = 70000 WHERE id = 1;
SELECT updated_at FROM employees WHERE id = 1;
-- reflects the current timestamp, set entirely by the trigger
BEFORE vs AFTER
BEFORE— runs before the change is written; can modifyNEW(as above) or even cancel the operation by returningNULL.AFTER— runs after the change is committed to the row; can't modify it, but is the right choice for side effects like writing an audit log entry.
CREATE OR REPLACE FUNCTION log_salary_change()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO salary_audit (employee_id, old_salary, new_salary, changed_at)
VALUES (OLD.id, OLD.salary, NEW.salary, now());
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_log_salary
AFTER UPDATE OF salary ON employees
FOR EACH ROW
WHEN (OLD.salary IS DISTINCT FROM NEW.salary)
EXECUTE FUNCTION log_salary_change();
WHEN scopes the trigger to only fire when salary actually changed, not on every unrelated update.
Advertisement728 x 90
Dropping a Trigger
DROP TRIGGER trg_set_updated_at ON employees;
Common Errors & Troubleshooting
ERROR: trigger function ... must return type trigger— a trigger function must be declaredRETURNS TRIGGER, and aBEFOREtrigger must actuallyRETURN NEW(orRETURN OLDfor a delete) — returning nothing/NULLfrom aBEFOREtrigger cancels the operation entirely.- Trigger firing more than expected — check whether it should really be
FOR EACH ROW(once per affected row) vsFOR EACH STATEMENT(once per statement, regardless of row count) — the wrong one causes it to run far more or fewer times than intended. - Infinite trigger loop — a trigger that itself performs an
UPDATEon the same table it's attached to (without a guard) can re-trigger itself; add aWHENcondition or check a flag to prevent recursive firing.
Key Takeaways
- A trigger is always paired with a separate trigger function —
CREATE TRIGGERjust says when to call it. BEFOREtriggers can modify the row (or cancel the operation);AFTERtriggers are for side effects like logging.WHEN (...)lets a trigger fire only under a specific condition, instead of every single time the event happens.
Advertisement320 x 100