mysql中的触发器事件:INSERT、UPDATE、DELETE

来源:这里教程网 时间:2026-02-28 20:44:09 作者:

触发器的三个事件分别对应什么操作

MySQL 触发器的

INSERT
UPDATE
DELETE
是指在对表执行这三类 DML 操作时自动触发的时机,不是“可以随便选”的选项,而是严格绑定到实际语句行为上:

INSERT
触发器:仅当执行
INSERT INTO ...
REPLACE INTO ...
(本质含 DELETE + INSERT)时触发;
LOAD DATA INFILE
也会触发,但需注意
LOCAL
是否启用影响权限路径
UPDATE
触发器:只响应字段值真正发生变化的行——即使写了
UPDATE t SET x=1 WHERE id=1
,但如果当前
x
已是
1
,该行不会触发
BEFORE UPDATE
AFTER UPDATE
DELETE
触发器:对
DELETE FROM
TRUNCATE TABLE
行为不一致——
TRUNCATE
是 DDL,**完全不触发任何 DELETE 触发器**,这是高频踩坑点

BEFORE 和 AFTER 在不同事件下的限制与用途

每个事件都可配

BEFORE
AFTER
,但作用差异极大,且有硬性约束:

BEFORE INSERT
:可修改
NEW
中的字段值(比如自动生成
created_at
或校验逻辑),但不能读写
OLD
(因为还没旧数据)
BEFORE UPDATE
:可同时读
OLD
、改
NEW
,适合实现“字段变更审计”或“级联更新前拦截”
AFTER DELETE
:能安全访问被删行的
OLD
数据,常用于写日志表或清理关联缓存;但此时原表中该行已不存在,不能反向恢复
AFTER INSERT
AFTER UPDATE
中禁止修改
NEW
字段(会报错
Can't update table 't' in stored function/trigger

常见错误:跨表操作、事务与性能隐患

触发器看似方便,但容易在无意识中引入严重问题:

在触发器里执行
INSERT INTO other_table
属于隐式跨表写入,若目标表有同名触发器,可能引发链式触发甚至死循环(MySQL 不做递归深度检查)
触发器运行在主 SQL 的同一事务中,一旦触发器内部出错(如违反外键、唯一约束),整个原始语句会回滚——但开发者常误以为“只是日志失败而已” 对高并发写入表加
AFTER UPDATE
触发器去更新统计表,会把热点从原表转移到统计表,造成锁争用;更稳妥做法是异步落库或用物化视图替代
MySQL 8.0.16+ 虽支持触发器中调用存储过程,但无法在触发器里显式开启/提交事务(
START TRANSACTION
报错),所有操作天然属于外层事务

一个安全的 UPDATE 触发器实操示例

场景:用户表

users
需记录每次邮箱变更的历史,且禁止将邮箱设为空字符串:

DELIMITER $$
CREATE TRIGGER users_email_audit
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
  IF NEW.email != OLD.email THEN
    INSERT INTO email_history (user_id, old_email, new_email, changed_at)
    VALUES (OLD.id, OLD.email, NEW.email, NOW());
  END IF;
  IF NEW.email = '' OR NEW.email IS NULL THEN
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Email cannot be empty';
  END IF;
END$$
DELIMITER ;

注意这里用了

SIGNAL
主动报错中断,比事后查日志更可控;
IF NEW.email != OLD.email
判断避免无意义插入——哪怕字段类型是
VARCHAR
,也要小心
NULL
与空字符串的比较行为。

真正难的不是写语法,而是想清楚“这一行变化是否真的需要立刻同步”,以及“如果触发器失败,业务能否接受整条语句失败”。

相关推荐