mysql触发器必须显式指定before或after:before可修改new、做校验拦截;after不可修改数据、适合日志同步;insert仅用new,delete仅用old,update两者皆可用;触发器属主事务,无全局顺序保证,慎防递归修改与事务一致性风险。

触发时机:before 和 after 的本质区别
MySQL 触发器在 INSERT、UPDATE、DELETE 发生时,**不是“自动选时间”,而是你必须显式指定 BEFORE 或 AFTER**。这个选择直接决定你能做什么、数据是否已落库、甚至能否修改操作本身。
-
BEFORE触发器在语句真正执行前运行,此时NEW可读可写(INSERT/UPDATE 中可改值),OLD在 UPDATE/DELETE 中只读;适合做数据校验、默认值填充、条件拦截(比如SET NEW.status = 'pending') -
AFTER触发器在语句成功提交后才执行,NEW和OLD都只读;适合做日志记录、跨表同步、库存扣减等依赖主表已变更的逻辑 - 误用
BEFORE去更新其他表(如库存),可能因主事务回滚导致不一致;而AFTER虽安全,但无法阻止非法数据入库——这是设计时必须权衡的点
INSERT/UPDATE/DELETE 各自能访问哪些数据别名
触发器内部靠 OLD 和 NEW 访问行数据,但三类操作支持的别名不同,写错会直接报错 Unknown column 'OLD.id' in 'field list'。
-
INSERT:只有NEW可用(新插入的整行),OLD不存在 -
DELETE:只有OLD可用(被删的原行),NEW不存在 -
UPDATE:OLD(更新前)和NEW(更新后)都可用,且字段名必须完全匹配原表定义(比如OLD.phone要求表里真有phone列) - 注意:
NEW在BEFORE INSERT中可赋值修改,但在AFTER中修改无效;OLD在任何情况下都不允许写入
触发顺序与事务一致性风险
MySQL 按「语句级」逐行触发,**没有全局统一顺序保证,且触发器本身属于主 SQL 所在事务**。这意味着:
- 一条
UPDATE ... WHERE id IN (1,2,3)会为每行分别触发一次BEFORE→ 执行更新 →AFTER,顺序取决于 InnoDB 的扫描路径(不一定是 ID 递增) - 如果
AFTER UPDATE触发器里又去UPDATE同一张表,极大概率报错Can't update table 'xxx' in stored function/trigger(MySQL 禁止触发表的递归修改) - 多个触发器同属一个事件(如两个
AFTER INSERT),执行顺序按创建时间先后,但 MySQL 不提供显式优先级控制;若逻辑强依赖顺序,应合并到一个触发器中
实际调试中最容易卡住的三个坑
新手常在这里浪费半天:不是语法错,而是机制理解偏差。
-
DELIMITER忘设或设错:在BEGIN...END块里写多条语句时,必须先用DELIMITER $$临时改结束符,否则 MySQL 在第一个分号就报错——这不是 bug,是解析器行为 - 试图在触发器里
SELECT ... INTO @var后再用SELECT @var返回结果:MySQL 5.7+ 明确禁止触发器返回结果集,只能赋值变量,且该变量无法被客户端直接读取 - 用
SHOW TRIGGERS查不到触发器:要么没在当前数据库下执行,要么触发器名含特殊字符未加反引号,或权限不足(需要TRIGGER权限)
触发器看似简单,但它的执行深度绑定在事务生命周期和行锁机制里。真正难的从来不是写法,而是想清楚:这条逻辑必须在落库前干预?还是等它稳了再补刀?有没有更轻量的替代方案(比如应用层监听 binlog)? 这些问题比 CREATE TRIGGER 多敲几个字重要得多。











