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,后来删了这行,旧数据不会自动补,新插入才受影响。这类隐性退化很难被测试覆盖到。
