只有 INSERT、UPDATE、DELETE 会触发 MySQL 触发器;LOAD DATA INFILE 和 REPLACE INTO 分别隐性触发 INSERT/DELETE+INSERT;INSERT IGNORE 和 ON DUPLICATE KEY UPDATE 按实际操作触发;BEFORE 可校验改写数据,AFTER 适合日志等不干预场景。

哪些 SQL 操作会真正触发 MySQL 触发器
只有明确执行了 INSERT、UPDATE、DELETE 这三类 DML 语句时,触发器才会被激活。其他操作如 SELECT、TRUNCATE、DROP TABLE、ALTER TABLE 都不会触发任何触发器。
但要注意两个“隐性触发”场景:
-
LOAD DATA INFILE会逐行插入数据,等效于多次INSERT,因此会触发INSERT型触发器(BEFORE INSERT或AFTER INSERT) -
REPLACE INTO实际是“先删后插”,会依次触发DELETE型和INSERT型触发器(如果对应表上存在)
而 INSERT IGNORE 和 ON DUPLICATE KEY UPDATE 属于 INSERT 的变体,只要实际发生了插入或更新行为,就会按规则触发对应类型的触发器。
BEFORE 和 AFTER 的本质区别与选型依据
BEFORE 是在 DML 操作真正写入/修改/删除数据前执行,你可以用它做校验、修正或拦截;AFTER 是在操作完成之后执行,适合日志记录、级联更新、统计同步等不干预原操作的场景。
关键限制在于:AFTER 触发器中无法修改 NEW 行(因为已落库),也无法访问被 DELETE 掉的行的当前状态(OLD 仍可用,但数据已删);而 BEFORE 中可以安全地 SET NEW.column = ... 来改写即将插入或更新的值。
- 想限制年龄不能为负?用
BEFORE INSERT+IF NEW.age - 想在用户注册后自动发欢迎邮件(需调用外部服务)?必须用
AFTER INSERT,且得确保该逻辑不阻塞主事务(MySQL 触发器内不能直接发 HTTP 请求,通常要写入队列表再由应用轮询) - 想防止某列被设为空?
BEFORE UPDATE中检查IF NEW.phone IS NULL THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'phone cannot be null';
触发器不是万能的:哪些情况它根本不会运行
MySQL 触发器是严格“行级”且“语句驱动”的,以下情况它完全静默:
- 通过
mysqldump导入数据(本质是批量INSERT语句,但若导入时加了--skip-triggers,则显式禁用) - 使用
INSERT ... SELECT从另一张表拉数据 —— 它会触发,但要注意:如果源表数据量大,触发器会为每一行执行一次,性能可能骤降 - 事务回滚(
ROLLBACK)时,AFTER触发器已执行的操作**不会自动回滚**!比如你在AFTER INSERT里往日志表插了一条记录,主事务回滚了,那条日志还在——这是常见数据不一致根源 - 对视图(
VIEW)执行 DML(即使视图可更新),触发器也不会触发,因为触发器只绑定在基表上
一个常被忽略的事实:触发器没有事务上下文继承
触发器本身运行在父语句的事务中,但它内部的 SQL 执行不具备独立事务能力。这意味着:
- 你不能在触发器里写
START TRANSACTION或COMMIT,MySQL 会直接报错ERROR 1305 (42000): SAVEPOINT does not exist类似错误 -
SIGNAL可以中断当前触发器并让整个语句失败,但无法部分回滚已执行的触发器语句(比如前面已经INSERT到日志表的那条记录) - 如果触发器里执行了耗时操作(如复杂子查询、多表 JOIN 更新),它会拖慢主 DML,且无法异步化——别指望它能“后台跑”
所以,真要实现强一致性级联(比如订单创建 → 库存扣减 → 积分增加),更稳妥的做法是把逻辑收口到应用层事务中,或用 AFTER 触发器仅写入一个“待处理事件表”,再由独立消费者处理,而非在触发器里直连多张业务表。










