DECLARE变量仅在声明它的BEGIN END块内有效,外层无法访问内层变量,内层同名变量会遮蔽外层变量,参数与DECLARE变量不可同名,跨块传值须用外层变量SET赋值。

DECLARE 变量只在当前 BEGIN END 块内有效
MySQL 存储过程中,DECLARE 定义的变量不是全局的,也不是跨块共享的——它严格绑定在声明它的那个 BEGIN ... END 块作用域内。外层块无法直接访问内层块声明的变量,内层块也看不到外层同名变量(除非显式用别名或标签规避)。
常见错误现象:Unknown column 'xxx' in 'field list' 或 Undeclared variable 报错,往往是因为在错误的作用域里引用了变量,比如在嵌套 IF 或循环块外读取了里面 DECLARE 的变量。
- 每个
BEGIN ... END块都构成独立作用域,包括IF、CASE、LOOP、WHILE内部的块 - 变量不能“向上穿透”:内层块声明的
DECLARE v INT DEFAULT 0;,外层块访问会报错 - 变量可以“向下隐藏”:外层已声明
v,内层再DECLARE v VARCHAR(10);,则内层所有对v的引用都指向新声明的版本
用块标签(label)配合 LEAVE / ITERATE 控制跳转时,不影响变量作用域
加标签(如 outer_loop: BEGIN)只是为了配合 LEAVE outer_loop 或 ITERATE 跳出多层嵌套,它本身不创建新作用域,也不改变变量可见性。变量仍由最近的 BEGIN ... END 块决定。
使用场景:写带多层 WHILE 嵌套的批量处理逻辑时,想从最内层直接跳出到外层块末尾——这时必须用标签,但别误以为加了标签就能让内层变量被外层读到。
- 标签名不参与作用域管理,只是跳转标记
-
LEAVE label只是控制执行流,不会“携带”变量出来 - 如果真需要把内层计算结果传给外层,必须用外层已声明的变量赋值,例如外层
DECLARE result INT;,内层用SET result = ...;
嵌套 BEGIN END 中重复 DECLARE 同名变量会覆盖而非报错
MySQL 允许在内层块中 DECLARE 和外层同名的变量,这不是错误,而是明确的“遮蔽(shadowing)”行为。内层块中对该变量的所有读写,都只操作内层声明的副本;外层变量保持不变,且在内层块中不可见。
容易踩的坑:调试时发现变量值“没变”,其实是改了内层副本,外层原变量根本没动;或者误以为 DECLARE v INT DEFAULT 1; 在多个分支里反复执行会重置变量——实际上每次进入该块才初始化一次,且仅限该块生命周期。
- 内层
DECLARE v INT DEFAULT 10;不会影响外层同名v的值 - 内层块退出后,其
DECLARE的变量自动销毁,内存释放 - 若需跨块传递状态,优先用外层声明 +
SET赋值,而不是依赖内层DECLARE
存储过程参数和 DECLARE 变量不能同名,否则报错
过程参数(IN p_id INT)属于整个存储过程的作用域,比任何内部 BEGIN END 都高一级。如果在过程体开头就 DECLARE p_id INT;,MySQL 会直接报错:ERROR 1336 (0A000): Variable or condition declaration after cursor or handler declaration 或更明确的 Duplicate parameter name 类提示(取决于版本)。
这是编译期检查,不是运行时错误。哪怕参数是 INOUT 或 OUT,也不允许与 DECLARE 同名。
- 参数名在整个过程体中占据顶层作用域,不可被
DECLARE覆盖 - 若逻辑需要临时变量存参数值,改名即可,例如
DECLARE v_p_id INT DEFAULT p_id; - 注意:用户变量(
@var)不受此限,但它走的是会话级变量机制,和DECLARE完全不同体系










