同表执行含UPDATE语句的触发器报错问题求助
为啥会报这个错?
你碰到的Can't update table 'Orders' in stored function/trigger because it is already used by statement which invoked this stored function/trigger报错,本质是MySQL的安全限制在起作用:当你执行的UPDATE语句触发了触发器时,这个语句已经在占用Orders表了,此时触发器内部再对同一张表做写操作(UPDATE/INSERT/DELETE),MySQL怕引发递归触发或者表锁冲突,就直接拦下来抛出这个错误了。
你的触发器代码里,在BEFORE UPDATE触发器中又写了UPDATE Orders语句,刚好撞在了这个限制上——哪怕最终行确实被更新了,这个报错还是会弹出来。
怎么改?得看你的实际需求
先拆解下你的代码逻辑:你想给Orders表中order_id最大的那一行的order_total加3.02,而且这个操作要在表有更新时自动触发。但你的写法有俩问题:一是触发器内直接更新同表违反了MySQL的限制;二是用NEW.order_total赋值给UPDATE的目标字段逻辑不对,NEW指的是当前正在被更新的那一行的副本,但你的WHERE条件是找最大order_id的行,这俩大概率不是同一行。
情况1:如果是要给当前正在更新的行加3.02
如果你的需求是,每次更新Orders表的某一行,就给这行的order_total加上3.02,那根本不需要写UPDATE语句!直接在BEFORE UPDATE触发器里修改NEW对象的字段就行——因为BEFORE UPDATE触发器允许修改NEW的字段值,这个修改会直接应用到最终的更新操作里:
DROP TRIGGER IF EXISTS UpdateTotal; DELIMITER | CREATE TRIGGER UpdateTotal BEFORE UPDATE ON Orders FOR EACH ROW BEGIN -- 直接修改当前要更新的行的order_total SET NEW.order_total = NEW.order_total + 3.02; END | DELIMITER ;
情况2:如果是要给表中order_id最大的行加3.02
如果你的需求是,只要Orders表有行被更新,就自动给表中order_id最大的那一行加3.02,这时候不能在触发器里直接更新同表,给你俩靠谱的方案:
方案A:用存储过程替代触发器
创建一个存储过程,先执行你原本的更新操作,再去更新最大order_id的行,这样就绕开了触发器的限制:
DELIMITER | CREATE PROCEDURE UpdateOrdersAndTotal( -- 这里定义你需要的参数,比如要更新的行的ID,以及要修改的字段值 IN target_order_id INT, IN new_column_value VARCHAR(255) -- 替换成你实际要更新的字段和类型 ) BEGIN -- 第一步:执行你原本要做的更新操作 UPDATE Orders SET your_column_name = new_column_value -- 替换成你的实际字段名 WHERE order_id = target_order_id; -- 第二步:更新order_id最大的那一行的order_total UPDATE Orders SET order_total = order_total + 3.02 WHERE order_id = (SELECT MAX(order_id) FROM Orders); END | DELIMITER ;
之后你调用这个存储过程来完成操作就行,别直接写UPDATE语句了:
CALL UpdateOrdersAndTotal(123, '你的新值'); -- 替换成你的实际参数
方案B:用AFTER UPDATE触发器+临时表(不推荐,谨慎用)
虽然能绕开限制,但可能会引发递归触发(比如更新最大行后又触发触发器),所以除非你确定逻辑不会无限循环,否则别用:
DROP TRIGGER IF EXISTS UpdateTotal; DELIMITER | CREATE TRIGGER UpdateTotal AFTER UPDATE ON Orders FOR EACH ROW BEGIN -- 先把最大的order_id存到临时表里 CREATE TEMPORARY TABLE IF NOT EXISTS TempMaxOrder (order_id INT); TRUNCATE TempMaxOrder; INSERT INTO TempMaxOrder SELECT MAX(order_id) FROM Orders; -- 借助临时表更新目标行 UPDATE Orders SET order_total = order_total + 3.02 WHERE order_id = (SELECT order_id FROM TempMaxOrder); -- 用完临时表记得删掉 DROP TEMPORARY TABLE IF EXISTS TempMaxOrder; END | DELIMITER ;
如果你用这个方案,记得把MySQL的递归触发关掉:SET max_sp_recursion_depth = 0;,防止无限触发。
最后总结
如果是修改当前正在更新的行,直接用情况1的写法就行,简单又安全;如果是要修改其他行,优先选存储过程的方案,尽量别在触发器里碰同表的写操作,避免踩MySQL的限制坑。
内容的提问来源于stack exchange,提问作者Alberto Muñoz Sánchez

