mysql触发器无独立事务,必须依附于触发它的dml语句所在事务,禁止显式事务控制(如commit、rollback、start transaction),仅允许savepoint和signal中断操作。

触发器根本没有独立事务上下文
MySQL 触发器不是独立的执行单元,它必须依附于触发它的那条 INSERT、UPDATE 或 DELETE 语句所处的事务。这条语句本身可能在显式事务中(如包裹在 START TRANSACTION 和 COMMIT 之间),也可能在自动提交模式下开启一个隐式事务——无论哪种情况,触发器的所有操作都自动加入该事务,无法“另起炉灶”。写 START TRANSACTION 就等于试图在一个已存在的事务里再开一个事务,MySQL 内核直接拒绝。
显式事务控制会破坏原子性保证
如果允许在触发器里写 COMMIT,就可能出现部分数据被“钉死”而主语句后续失败的情况。例如:触发器往日志表插入一条记录后 COMMIT,紧接着主 INSERT 因外键约束失败回滚——日志留下了,主表没写成,数据不一致。MySQL 的设计原则是:整个 DML + 所有触发逻辑,要么全成功,要么全失败。所以它在解析阶段就拦截所有 COMMIT、ROLLBACK、START TRANSACTION,报 ERROR 1422 或 ERROR 1305,而不是等到执行时才出错。
连语法层面都不允许,哪怕包在 IF FALSE 里
- 只要触发器定义中出现
COMMIT关键字(哪怕被IF FALSE THEN COMMIT; END IF;包裹),MySQL 在CREATE TRIGGER阶段就会报错,根本不会创建成功 - 同理,
TRUNCATE TABLE、ALTER TABLE、对 MyISAM 表的操作,也会触发隐式提交,同样被禁止 -
SAVEPOINT是唯一被允许的事务控制语句,但它只能用于局部回滚(比如触发器内某步失败后撤回自己做的修改),不能替代COMMIT
想中止操作?用 SIGNAL,别碰 ROLLBACK
很多人想“只回滚触发器里的动作”,于是写 ROLLBACK,结果报错甚至误以为是函数缺失(ERROR 1305: FUNCTION does not exist)。正确做法是在 BEFORE 触发器中用 SIGNAL 主动抛异常:
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Amount cannot be negative';
这会让 MySQL 立即中断当前 DML,并自动回滚整个事务——包括原始语句和触发器内所有已执行操作。注意:AFTER 触发器里 SIGNAL 已无意义,因为数据早已落盘;且 MySQL 5.6 及更早版本不支持 SIGNAL,需换其他方式(但不推荐)。
sql_mode 非严格时,很多赋值或类型转换会静默失败,导致校验逻辑形同虚设。调试时优先查 SHOW ERRORS LIMIT 1,再分段模拟 NEW/OLD 值,别依赖客户端提示。











