Build the mental model
SQL and PL/pgSQL functions keep reusable logic near data, while triggers run automatically on INSERT, UPDATE, or DELETE. They suit audit fields, invariants, and some derived values, but excessive hidden side effects are hard to debug. Do not hide entire business workflows in triggers.
Connect it to a real scenario
Create a trigger function that maintains `updated_at`. Consider its `search_path`, privileges, and `SECURITY DEFINER` risks, and use schema-qualified object names.
Try the working example
CREATE FUNCTION app.set_updated_at()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.updated_at := clock_timestamp();
RETURN NEW;
END;
$$;
ALTER TABLE app.tutorials
ADD COLUMN updated_at timestamptz NOT NULL DEFAULT now();
CREATE TRIGGER tutorials_set_updated_at
BEFORE UPDATE ON app.tutorials
FOR EACH ROW EXECUTE FUNCTION app.set_updated_at();Every tutorial update automatically changes `updated_at`.5-minute try-it
Test whether a no-op update changes the timestamp and document the semantics you want.
One important caution
Developers unaware of trigger execution can struggle to explain unexpected writes. Document and test trigger behavior.
PostgreSQL — PL/pgSQL — PostgreSQL Global Development Group