BEFORE触发器可修改NEW行并中止操作,AFTER触发器只读且无法阻止主DML;前者用于默认值、校验等前置控制,后者适用于日志、统计等后置动作。

mysql触发器before和after有什么区别_mysql执行顺序说明  第1张

BEFORE 触发器能改数据,AFTER 不能

这是最核心的区别:在 BEFORE INSERTBEFORE 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,只有 OLDINSERT 没有 OLD,只有 NEWUPDATE 两者都有

执行顺序决定能不能“拦住非法操作”

MySQL 的执行链条是严格线性的:BEFORE → 实际 DML → AFTER。这意味着:

  • BEFORE 在语句执行前介入,可以用 IF + SIGNAL 主动中止整个操作(比如年龄为负就拒绝插入)
  • AFTER 永远在成功提交后才运行,哪怕你想“回滚”,也已经晚了——它无法阻止主操作发生
  • 典型翻车场景:AFTER INSERT 更新库存,但订单插入时库存已不足 → 爆库;换成 BEFORE INSERT 先查库存再决定是否放行,才能守住一致性

什么时候必须用 BEFORE,什么时候只能用 AFTER

选错时机不是语法错误,而是逻辑漏洞。关键看你要做什么:

  • 必须用 BEFORE
    – 自动填充默认值(如 idcreated_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 所在事务中,这点极易被忽视:

  • BEFOREAFTER 都受同一事务控制:主语句回滚,触发器所有操作也一并回滚
  • AFTER 里做耗时操作(如远程 HTTP 请求、大表 JOIN)会拖慢主事务,增加锁持有时间
  • 所有触发器都禁止调用存储函数以外的非确定性函数(如 NOW() 可用,UUID() 在某些版本受限)
  • MySQL 8.0+ 支持多触发器同类型排序(FOLLOWS/PRECEDES),但多数业务应避免依赖顺序,尽量单触发器闭环

真正难的从来不是语法,而是想清楚:这一步动作,是该“卡在门口检查”,还是“等进门后再记一笔”。顺序错了,数据就不可信。