BEFORE 触发器能改数据,AFTER 不能
这是最核心的区别:在
BEFORE INSERT或
BEFORE UPDATE中,你可以安全地修改
NEW行的字段值,这些修改会真正写入表;而
AFTER触发器里,
NEW是只读的——你试图赋值会报错:
ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger。
BEFORE中可写:
SET NEW.created_at = NOW(), NEW.email = LOWER(NEW.email);
AFTER中写同句 → 直接报错
DELETE没有
NEW,只有
OLD;
INSERT没有
OLD,只有
NEW;
UPDATE两者都有
执行顺序决定能不能“拦住非法操作”
MySQL 的执行链条是严格线性的:BEFORE → 实际 DML → AFTER。这意味着:
BEFORE在语句执行前介入,可以用
IF+
SIGNAL主动中止整个操作(比如年龄为负就拒绝插入)
AFTER永远在成功提交后才运行,哪怕你想“回滚”,也已经晚了——它无法阻止主操作发生 典型翻车场景:
AFTER INSERT更新库存,但订单插入时库存已不足 → 爆库;换成
BEFORE INSERT先查库存再决定是否放行,才能守住一致性
什么时候必须用 BEFORE,什么时候只能用 AFTER
选错时机不是语法错误,而是逻辑漏洞。关键看你要做什么:
必须用BEFORE:
– 自动填充默认值(如
id、
created_at)
– 格式化输入(转小写、去空格、脱敏)
– 数据校验并中断非法写入 只能用
AFTER:
– 写审计日志(要确保主操作已落地)
– 更新统计表(如销量+1,需确认订单真实插入)
– 调用外部系统(如发消息),不能因下游失败拖垮主事务
DELIMITER $$
CREATE TRIGGER order_before_check
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF NEW.quantity > (SELECT stock FROM products WHERE id = NEW.product_id) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insufficient stock';
END IF;
END$$
DELIMITER ;
别忽略事务边界和性能影响
触发器天然运行在主 DML 所在事务中,这点极易被忽视:
BEFORE和
AFTER都受同一事务控制:主语句回滚,触发器所有操作也一并回滚 但
AFTER里做耗时操作(如远程 HTTP 请求、大表 JOIN)会拖慢主事务,增加锁持有时间 所有触发器都禁止调用存储函数以外的非确定性函数(如
NOW()可用,
UUID()在某些版本受限) MySQL 8.0+ 支持多触发器同类型排序(
FOLLOWS/
PRECEDES),但多数业务应避免依赖顺序,尽量单触发器闭环
真正难的从来不是语法,而是想清楚:这一步动作,是该“卡在门口检查”,还是“等进门后再记一笔”。顺序错了,数据就不可信。
