想改数据或拦住非法操作,用 BEFORE;想记日志、发通知、更新统计表,用 AFTER。BEFORE 可修改 NEW 实现校验与默认值填充,AFTER 可安全读取已提交数据做联动操作,但不可修改触发源表,否则报错或拖垮事务。

直接说结论:想改数据或拦住非法操作,用 BEFORE;想记日志、发通知、更新统计表,用 AFTER。选错时机不是功能失效,而是触发器要么改不了数据,要么报错退出,甚至拖垮整个事务。
BEFORE 触发器:能改 NEW,但不能读未提交的最终状态
BEFORE 在语句真正写入前运行,此时 NEW 可写、OLD 可读(UPDATE/DELETE),但事务还没提交,其他会话看不到这些变更。
- 适合给缺失字段填默认值:
NEW.created_at := NOW()(注意 PostgreSQL 用:=赋值) - 业务校验失败必须用
RAISE EXCEPTION中断,不能靠返回NULL—— PostgreSQL 不认这个逻辑 - INSERT 里没有
OLD,UPDATE/DELETE 里没有NEW,硬引用会报column "old.id" does not exist - 避免在
BEFORE里查其他表做复杂判断,尤其带FOR UPDATE的 SELECT,容易和主事务争锁
AFTER 触发器:数据已落盘,但不能再碰当前行
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 特有陷阱:函数体、触发器声明、权限三者必须匹配
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插入失败会导致主事务回滚 - 别用超级用户创建触发器函数,生产环境应限定最小权限角色
最常被忽略的一点:PostgreSQL 的 BEFORE 触发器里改 NEW 字段,只影响当前触发器后续逻辑和最终写入值,不影响同一语句中其他 BEFORE 触发器对 NEW 的读取——它们看到的是同一个内存副本,不是“链式传递”。这点和 MySQL 不同,调试时得盯紧执行顺序。

















