INSERT触发器在行级写入前/后执行:BEFORE INSERT可修改NEW值但不能读未赋值的自增ID,AFTER INSERT才可安全访问生成主键;ON DUPLICATE KEY UPDATE时BEFORE INSERT仍触发,冲突则转为UPDATE触发器。

INSERT 触发器在什么时机真正执行?
MySQL 的 BEFORE INSERT 和 AFTER INSERT 触发器不是在语句解析后立刻运行,而是在行级写入前/后触发——这意味着:
-
BEFORE INSERT可修改NEW值(如自动填充created_at、校验字段非空),但不能读取NEW.id(若为自增且未显式赋值) -
AFTER INSERT才能安全访问生成的主键值(如NEW.id),也才能对其他表做关联插入 - 若使用
INSERT ... ON DUPLICATE KEY UPDATE,BEFORE INSERT仍会触发,但冲突时不会走AFTER INSERT,而是可能触发BEFORE UPDATE/AFTER UPDATE(取决于是否更新)
常见错误:在 BEFORE INSERT 中尝试 SELECT ... FROM same_table 会报错 Can't update table 't' in stored function/trigger,这是 MySQL 的限制,不是权限问题。
UPDATE 触发器如何判断字段是否“真被修改”?
MySQL 触发器不自动识别语义上的变更。哪怕写 UPDATE t SET status = status,只要语法合法,BEFORE UPDATE 和 AFTER UPDATE 都会执行。
- 判断某字段是否变化,必须显式比较:
IF OLD.status != NEW.status THEN ... - 注意 NULL 比较:
OLD.col = NEW.col在任一为 NULL 时结果为 NULL(即 false),应改用NOT (OLD.col NEW.col) - 多字段批量更新时,每个字段都要单独判断;没写进 SET 子句的字段,
NEW.col等于OLD.col,但不会被自动跳过
性能影响:触发器中执行复杂查询或调用函数(尤其是涉及大表 JOIN 或子查询)会显著拖慢 UPDATE 速度,且无法被索引优化绕过。
DELETE 触发器无法阻止级联删除的陷阱
BEFORE DELETE 触发器可以抛出异常(如 SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Forbidden')来中止单行删除,但以下情况它无能为力:
- 外键定义了
ON DELETE CASCADE:父表行删除时,子表匹配行被自动删掉,不会触发子表的 DELETE 触发器 - 使用
TRUNCATE TABLE:不走触发器,不记 binlog(在 ROW 格式下),且无法回滚 - 事务中批量
DELETE FROM t WHERE ...:触发器对每行生效,但如果 WHERE 条件误写导致删多,触发器本身不能“预估影响行数”来拦截
安全风险点:依赖触发器做数据删除审计时,必须确认业务层从未使用 TRUNCATE,且所有外键都显式设为 ON DELETE RESTRICT 或 NO ACTION。
触发器里的事务边界和错误传播很脆弱
MySQL 触发器运行在当前语句的事务上下文中,但它自身的错误处理能力有限:
- 触发器内发生未捕获异常(如除零、列不存在、存储过程 SIGNAL),整个外部语句失败并回滚
- 但无法在触发器里启动独立事务(
START TRANSACTION报错),也不能用COMMIT/ROLLBACK—— 这些操作会被忽略或报错 - 如果触发器调用存储函数,而该函数含
SELECT ... FOR UPDATE,可能引发死锁,尤其在高并发更新同一主键范围时
最容易被忽略的一点:触发器代码变更后,不会自动失效已存在的数据约束逻辑。比如原来靠 BEFORE INSERT 强制设置 updated_at,后来删了这行,旧数据不会自动补,新插入才受影响。这类隐性退化很难被测试覆盖到。










