应根据用途选择触发器类型:BEFORE用于修改数据或校验,AFTER用于日志、通知或统计更新;选错会导致报错、静默失效或死锁。

想改数据或拦住非法操作,用 BEFORE;想记日志、发通知、更新统计表,用 AFTER。选错不是功能不生效,而是直接报错(比如 AFTER 里写 UPDATE 触发源表)、静默失效(BEFORE 函数漏 RETURN NEW 导致插入丢数据),或者拖垮事务(BEFORE 里查表加锁引发死锁)。
BEFORE 触发器:能改 NEW,但不能读最终状态
它在语句真正写入前运行,NEW 可写、OLD 可读(UPDATE/DELETE),但事务还没提交,其他会话看不到这些变更。
- 填默认值:
NEW.created_at := NOW()(PostgreSQL 用:=赋值) - 业务校验失败必须用
RAISE EXCEPTION中断,返回NULL不起作用 -
INSERT里没有OLD,UPDATE/DELETE里没有NEW,硬引用会报column "old.id" does not exist - 避免在
BEFORE里查其他表做复杂判断,尤其带FOR UPDATE的SELECT,容易和主事务争锁
AFTER 触发器:数据已落盘,但不能再碰当前行
运行时主语句已成功提交,NEW 和 OLD 都是确定值,可放心用于日志或关联查询,但任何尝试修改触发源表的 SQL(比如 UPDATE target_table SET ...)都会触发错误 ERROR: cannot update table "xxx" in AFTER ROW trigger。
- 审计日志最稳妥:
INSERT INTO audit_log () VALUES (NEW.id, 'INSERT', NOW()) - 跨表统计更新必须用
AFTER,比如订单插入后更新order_summary表的计数器 - 要调外部服务,别在
AFTER里直接发 HTTP 请求——事务已结束,连接池可能被回收,建议写消息队列再由消费者处理 - PostgreSQL 的
AFTER默认是行级(FOR EACH ROW),1000 行UPDATE就执行 1000 次,性能敏感场景考虑改用FOR EACH STATEMENT+ 自定义聚合逻辑
函数体、触发器声明、权限三者必须匹配
PostgreSQL 要求触发器必须绑定一个 RETURNS trigger 函数,且该函数里不能漏掉 RETURN NEW(BEFORE)或 RETURN NULL(AFTER),否则会报 function must return a trigger value。
-
BEFORE函数末尾必须写RETURN NEW(INSERT/UPDATE)或RETURN OLD(DELETE),否则新行不会写入 -
AFTER函数末尾写RETURN NULL,写RETURN NEW会被忽略但不报错——容易误以为逻辑生效了 - 触发器函数所属角色,必须对日志表有
INSERT权限,否则AFTER插入失败会导致主事务回滚 - 别用超级用户创建触发器函数,生产环境应限定最小权限角色
最常被忽略的一点:BEFORE 里改 NEW 字段,只影响当前行写入
这个动作本身不触发二次校验,也不自动传播到后续触发器。如果业务逻辑依赖多个字段联动(比如改了 status 就得同步算 updated_at 和 version),所有计算必须在同一个 BEFORE 函数里做完,不能拆到多个触发器里靠顺序保证——PostgreSQL 不保证多触发器执行顺序。

















