动态sql中declare局部变量无法被prepare/execute识别,必须转为@用户变量;拼接sql需用ifnull兜底null;命名须隔离(如l_前缀);declare必须紧邻begin;嵌套块变量不共享且会遮蔽。

动态SQL在存储过程中读不到变量,不是语法写错,而是作用域和执行机制根本不同——PREPARE和EXECUTE只认@用户变量,完全无视DECLARE声明的局部变量。
PREPARE根本不解析DECLARE变量
MySQL的PREPARE语句在运行时只接受字面字符串或以@开头的用户变量。你写DECLARE v_id INT DEFAULT 123;,再塞进EXECUTE stmt USING v_id,它不会报错,但传进去的是NULL,最终查不到数据或触发Unknown column 'v_id'错误。
- 必须显式转成用户变量:
SET @v_id = v_id;,再EXECUTE stmt USING @v_id; - 拼接方式也得用
@变量:SET @sql = CONCAT('SELECT * FROM t WHERE id = ', @v_id);,不能直接拼v_id -
CONCAT()里若含NULL值(比如未初始化的v_str),整个结果变NULL;要用IFNULL(v_str, '')或COALESCE(v_str, '')兜底
局部变量和@变量同名会互相干扰
当你同时有DECLARE sum DECIMAL;和SET @sum = 0;,后续写SELECT sum;读的是局部变量,SELECT @sum;才是用户变量。更危险的是SET sum = @sum + 1;这种写法——左侧没@,MySQL默认绑定到局部变量sum,@sum根本没被更新。
- 赋值操作符
:=左侧不带@,永远优先匹配最近作用域的局部变量 - 累加逻辑别用
@sum := @sum + val,改用SET @sum = IFNULL(@sum, 0) + val; - 命名隔离最可靠:局部变量统一加
l_前缀(如l_total),用户变量坚持用@且带业务含义(如@batch_start_time)
嵌套块里变量声明位置一错全错
DECLARE必须紧贴BEGIN之后,中间插一行SELECT或IF就立刻报ERROR 1064。嵌套块也不能复用外层声明——每个BEGIN ... END都得自己把DECLARE放在最前面。
- 错误示范:
BEGIN SELECT 1; DECLARE x INT;→ 直接失败 - 正确顺序:
BEGIN DECLARE x INT DEFAULT 0; SET x = 1; - 嵌套块内同名变量会静默遮蔽外层变量,调试时看不出异常,只在逻辑错乱后才暴露
真正容易被忽略的点是:局部变量无法跨PREPARE/EXECUTE边界,而用户变量又容易因同名遮蔽或未初始化导致值丢失。别指望“变量声明了就能用”,得靠命名规范+显式转换+作用域检查三者配合才能稳住。











