# info > CREATE_TRIGGER

---
type: CommandReference
command: CREATE TRIGGER
mode: man
section: 7
source: man-pages
---

## Quick Reference

- `CREATE TRIGGER ... BEFORE UPDATE ON accounts ...` — Execute function before row update.
- `CREATE OR REPLACE TRIGGER ... BEFORE UPDATE OF balance ON accounts ...` — Modify trigger to fire only on specific column.
- `CREATE TRIGGER ... BEFORE UPDATE ON accounts ... WHEN (OLD.balance IS DISTINCT FROM NEW.balance)` — Fire only if column value changed.
- `CREATE TRIGGER ... AFTER UPDATE ON accounts ... WHEN (OLD.* IS DISTINCT FROM NEW.*)` — Log updates only if something changed.
- `CREATE TRIGGER ... INSTEAD OF INSERT ON my_view ...` — Execute function instead of insert on view.
- `CREATE TRIGGER ... AFTER INSERT ON transfer REFERENCING NEW TABLE AS inserted ...` — Statement-level trigger with transition relation.

## Name

`CREATE_TRIGGER` — define a new trigger

## Synopsis

shell
CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
    ON table_name
    [ FROM referenced_table_name ]
    [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
    [ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]
    [ FOR [ EACH ] { ROW | STATEMENT } ]
    [ WHEN ( condition ) ]
    EXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )
where `event` is: `INSERT`, `UPDATE [ OF column_name [, ...] ]`, `DELETE`, `TRUNCATE`

## Options

- `name` — Name of trigger, must be unique per table.
- `BEFORE`, `AFTER`, `INSTEAD OF` — When trigger fires relative to event.
- `event` — `INSERT`, `UPDATE`, `DELETE`, `TRUNCATE`; multiple events via `OR`.
- `table_name` — Table, view, or foreign table.
- `referenced_table_name` — For foreign-key constraint triggers (not recommended).
- `DEFERRABLE`, `NOT DEFERRABLE`, `INITIALLY IMMEDIATE`, `INITIALLY DEFERRED` — Constraint trigger timing.
- `REFERENCING OLD TABLE / NEW TABLE transition_relation_name` — Access transition relations (AFTER triggers only).
- `FOR EACH ROW` / `FOR EACH STATEMENT` — Row-level vs statement-level firing.
- `WHEN (condition)` — Boolean expression to conditionally fire trigger.
- `function_name` — User-supplied function taking no arguments, returning `trigger` type.
- `arguments` — Optional comma-separated literal string constants passed to function.

## Notes

- Requires `TRIGGER` privilege on table and `EXECUTE` on trigger function.
- Use `DROP TRIGGER` to remove.
- Row-level triggers on partitioned tables create clone triggers on partitions.
- Column-specific triggers fire when listed columns are targets in `UPDATE SET`.
- `WHEN` condition in BEFORE triggers evaluated just before function execution; in AFTER triggers, determines queuing.
- Single SQL command can fire multiple trigger types (e.g., `INSERT` with `ON CONFLICT DO UPDATE`).
- Foreign-key cascading actions (`CASCADE`, `SET NULL`) can trigger additional triggers.
- Statement-level triggers on views only fire if action handled by row-level `INSTEAD OF` trigger.
- Modifying partitioned table fires statement-level triggers on named table, not partitions; row-level triggers fire on affected partitions.
- `OR REPLACE` not supported for constraint triggers.
- PostgreSQL implements a subset of SQL standard (missing transition tables with column-specific triggers, multiple SQL commands as triggered action, etc.).

Allowed trigger types:

| When | Event | Row-level | Statement-level |
|------|-------|-----------|-----------------|
| BEFORE | INSERT/UPDATE/DELETE | Tables, foreign tables | Tables, views, foreign tables |
| BEFORE | TRUNCATE | - | Tables |
| AFTER | INSERT/UPDATE/DELETE | Tables, foreign tables | Tables, views, foreign tables |
| AFTER | TRUNCATE | - | Tables |
| INSTEAD OF | INSERT/UPDATE/DELETE | Views | - |
| INSTEAD OF | TRUNCATE | - | - |

## Examples

Execute function before update on each row:

shell
CREATE TRIGGER check_update
    BEFORE UPDATE ON accounts
    FOR EACH ROW
    EXECUTE FUNCTION check_account_update();
Modify trigger to fire only when balance column is updated:

shell
CREATE OR REPLACE TRIGGER check_update
    BEFORE UPDATE OF balance ON accounts
    FOR EACH ROW
    EXECUTE FUNCTION check_account_update();
Fire only if balance actually changed:

shell
CREATE TRIGGER check_update
    BEFORE UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.balance IS DISTINCT FROM NEW.balance)
    EXECUTE FUNCTION check_account_update();
Log updates if anything changed:

shell
CREATE TRIGGER log_update
    AFTER UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.* IS DISTINCT FROM NEW.*)
    EXECUTE FUNCTION log_account_update();
INSTEAD OF trigger on view:

shell
CREATE TRIGGER view_insert
    INSTEAD OF INSERT ON my_view
    FOR EACH ROW
    EXECUTE FUNCTION view_insert_row();
Statement-level trigger with transition relation:

shell
CREATE TRIGGER transfer_insert
    AFTER INSERT ON transfer
    REFERENCING NEW TABLE AS inserted
    FOR EACH STATEMENT
    EXECUTE FUNCTION check_transfer_balances_to_zero();
## See Also

- [ALTER TRIGGER](http://localhost/phpMan.php/man/ALTERTRIGGER/7/markdown)
- [DROP TRIGGER](http://localhost/phpMan.php/man/DROPTRIGGER/7/markdown)
- [CREATE FUNCTION](http://localhost/phpMan.php/man/CREATEFUNCTION/7/markdown)
- [SET CONSTRAINTS](http://localhost/phpMan.php/man/SETCONSTRAINTS/7/markdown)