触发器类型按执行时机(before/after)与事件(insert/update/delete)组合确定,共6种;选错时机将导致逻辑错误,如校验新值须用before,日志记录须用after。

INSERT/UPDATE/DELETE 触发器怎么选
触发器类型不是按功能分,而是按它「在什么动作发生时执行」来定的。MySQL 和 PostgreSQL 都只支持 BEFORE 和 AFTER 两种时机,再叠加上 INSERT、UPDATE、DELETE 三类事件,组合出最多 6 种写法(比如 BEFORE INSERT、AFTER UPDATE)。
选错时机会导致逻辑失败:比如想用新值校验但用了 BEFORE UPDATE,这时 NEW 已可用;但若用 AFTER UPDATE,就只能读不能改——因为语句已执行完。
-
BEFORE INSERT:适合做默认值填充、字段格式标准化(如转小写)、或拒绝非法数据(用SIGNAL抛错) -
AFTER INSERT:适合记录日志、更新统计表、调用存储过程发通知——不能改当前行数据 -
BEFORE UPDATE:能安全修改NEW字段,比如自动更新updated_at或归一化邮箱格式 -
BEFORE DELETE:可用来备份待删行(插入到历史表),或检查外键约束逻辑
MySQL 创建触发器的硬性限制
MySQL 对触发器有几条不声张但致命的限制,踩中直接报错且提示模糊:
- 一张表上同一种事件+时机只能有一个触发器,比如不能有两个
BEFORE INSERT;想实现多逻辑,得全写进一个触发器里 - 不能在触发器里对触发它的那张表做
INSERT/UPDATE/DELETE(会报ERROR 1442),这是 MySQL 的死锁防护机制,不是 bug - 触发器里不能调用含非确定性函数的存储函数,比如
NOW()是允许的,但UUID()在某些版本会报错 - 触发器体必须用
DELIMITER改分隔符,否则遇到第一个;就被截断——这是新手最常卡住的地方
示例(MySQL):
千博购物系统.Net能够适合不同类型商品,为您提供了一个完整的在线开店解决方案。千博购物系统.Net除了拥有一般网上商店系统所具有的所有功能,还拥有着其它网店系统没有的许多超强功能。千博购物系统.Net适合中小企业和个人快速构建个性化的网上商店。强劲、安全、稳定、易用、免费是它的主要特性。系统由C#及Access/MS SQL开发,是B/S(浏览器/服务器)结构Asp.Net程序。多种独创的技术使
DELIMITER $$ CREATE TRIGGER user_updated_at BEFORE UPDATE ON users FOR EACH ROW BEGIN SET NEW.updated_at = NOW(); END$$ DELIMITER ;
PostgreSQL 的 WHEN 条件和 REFERENCING
PostgreSQL 触发器更灵活,但语法细节容易漏掉。关键点不在“能不能”,而在“怎么写才生效”:
-
WHEN子句必须用括号包裹条件表达式,比如WHEN (OLD.status != NEW.status),漏括号直接语法错误 - 想在触发器函数里访问旧/新行,必须在
CREATE TRIGGER里显式声明REFERENCING OLD AS old NEW AS new,否则函数内OLD/NEW不可见 - 触发器函数必须返回
TRIGGER类型,且最后一行得是RETURN NEW(BEFORE)或RETURN NULL(AFTER),否则可能静默失败 - PostgreSQL 允许同一事件多个触发器,执行顺序按名字字母序——别依赖顺序,名字别起成
a_check和z_log
触发器里的错误处理很脆弱
触发器里抛错不像应用层那么可控:一旦出错,整个原始 SQL 事务会回滚,而且错误信息往往只显示“触发器执行失败”,不带具体行号或变量值。
- MySQL 中用
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'xxx'才能自定义错误;直接SELECT或INSERT报错只会显示通用提示 - PostgreSQL 中建议在触发器函数开头加
RAISE EXCEPTION,但注意它会让整个事务终止,连之前成功的 INSERT 都撤回 - 别在触发器里做远程 HTTP 调用或大表 JOIN——超时或锁等待会拖垮主 SQL,且无法重试
- 测试时务必用真实数据量跑,小数据下没问题的触发器,百万行批量更新时可能慢 10 倍以上
真正麻烦的是:触发器逻辑藏在数据库里,应用代码看不到,上线后排查问题经常要翻三处——应用日志、binlog、还有 SHOW CREATE TRIGGER。









