BEFORE触发器可修改NEW行并中止操作,AFTER触发器只读且无法阻止主DML;前者用于默认值、校验等前置控制,后者适用于日志、统计等后置动作。
这是最核心的区别:在 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:id、created_at)AFTER: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() 在某些版本受限)FOLLOWS/PRECEDES),但多数业务应避免依赖顺序,尽量单触发器闭环真正难的从来不
