mysql触发器中不能直接用存储过程的out参数赋值给new字段,必须通过用户变量(如@tmp_full)中转,因out参数仅支持绑定用户变量或系统变量,不支持new/old伪记录字段。

触发器里不能直接用存储过程的 OUT 参数赋值给 NEW 字段
MySQL 5.7 触发器中调用带 OUT 参数的存储过程本身语法合法,但无法把输出值直接写入 NEW.full_name 这类触发器上下文变量——因为 OUT 参数只能绑定到用户变量(如 @tmp_full),不能指向触发器的 NEW 或 OLD 伪记录字段。
常见错误现象:ERROR 1327 (42000): Undeclared variable: NEW 或赋值后 NEW.full_name 仍为 NULL。
-
CALL set_full_name(NEW.first_name, NEW.last_name, NEW.full_name)会报错:MySQL 不允许把NEW.xxx当作变量地址传给OUT参数 - 必须中转:先
CALL set_full_name(..., @tmp_full),再SET NEW.full_name = @tmp_full - 用户变量
@tmp_full在触发器内无需声明,但作用域仅限当前触发器执行周期
为什么不能用局部变量替代 @tmp_full?
触发器中声明的局部变量(DECLARE v_full VARCHAR(101))无法被 CALL 语句的 OUT 参数引用——MySQL 要求 OUT 参数实参必须是“可赋值的变量”,而局部变量不满足该约束,只有用户变量(@xxx)和系统变量(@@xxx)才支持。
实操建议:
- 别写
DECLARE v_full VARCHAR(101); CALL set_full_name(..., v_full)—— 会报ERROR 1327 - 坚持用
@tmp_full中转,且不必SET @tmp_full = ''初始化,CALL会自动覆盖 - 如果需多处复用,可在触发器开头统一
SET @tmp_full = NULL,避免残留值干扰
输出参数中转引发的隐性时序问题
用户变量中转看似简单,但在 AFTER 触发器或涉及多个触发器链式调用时,@tmp_full 可能被后续触发器意外覆盖,导致字段赋值错乱。
关键限制:
-
@tmp_full是会话级变量,同一连接中所有触发器共享它 - 若表上有
BEFORE INSERT和AFTER INSERT两个触发器,且都用@tmp_full,后者可能读到前者留下的旧值 - 并发插入时,不同行共用同一个
@tmp_full,无隔离性保障
更稳妥的做法是:把拼接逻辑全移到存储过程中,用 SELECT CONCAT(...) INTO NEW.full_name 直接在触发器里完成,绕过 OUT 参数中转。
真正该警惕的是嵌套调用中的事务污染
比参数中转更危险的是存储过程内部行为——哪怕你正确用了 @tmp_full,只要被调用的存储过程含 COMMIT、操作 MyISAM 表、或修改了触发器所在表,就会触发 ERROR 1422 或 ERROR 1442。
检查点:
- 用
SHOW CREATE PROCEDURE proc_name确认过程里没START TRANSACTION、COMMIT、ROLLBACK - 确认过程只读或只写其他 InnoDB 表,绝不碰
NEW所属的那张表 - 避免在存储过程中查
SELECT ... FROM users WHERE id = NEW.id FOR UPDATE—— 即使是SELECT,也可能因锁机制触发ERROR 1442
输出参数只是表象,底层事务隔离与表锁定规则才是触发器调用失败的真正门槛。











