mysql存储过程的declare必须紧接begin后连续声明所有变量、游标和处理器,顺序为:done标志→游标→continue handler;嵌套块需各自独立声明;游标字段名不可与接收变量同名。

DECLARE 必须在 BEGIN 后立即出现,这是语法硬性限制
MySQL 解析器在遇到 BEGIN 后,会严格按固定顺序扫描后续语句:所有 DECLARE(变量、游标、条件、处理器)必须连续出现在最前面,中间不能夹杂任何可执行语句。一旦出现 SELECT、SET、IF 等,解析立刻失败,报错 ERROR 1337 (42000): Variable or condition declaration after cursor or handler declaration。
这不是风格建议,而是 MySQL 服务端的词法分析规则——它把 DECLARE 块当作“元信息区”,把后续所有内容当作“执行区”,两者不可交错。
游标和 HANDLER 的声明顺序有强依赖关系
游标本身不执行查询,只保存 SELECT 语句文本;而 CONTINUE HANDLER FOR NOT FOUND 是为游标 FETCH 行为绑定异常响应。这两者必须成对紧邻声明,且 done 标志变量必须在它们之间定义,否则 HANDLER 无法修改该变量。
-
DECLARE done BOOLEAN DEFAULT FALSE;必须在游标声明前或之间(推荐之间) -
DECLARE cur CURSOR FOR ...必须在done之后、HANDLER之前 -
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;必须紧跟游标声明后
如果把 DECLARE cur 放在 HANDLER 后面,MySQL 会认为你试图在“执行区”里再插一条声明语句,直接拒绝解析。
嵌套块里也得各自遵守这套顺序
很多人以为外层 BEGIN 里声明一次变量就够了,结果在内层块里复用同名变量,导致作用域混乱或赋值覆盖。实际上每个 BEGIN ... END 都是独立作用域,每个都必须重走一遍“先 DECLARE,再执行”的流程。
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
例如两个嵌套游标逻辑:
BEGIN DECLARE done1 BOOLEAN DEFAULT FALSE; DECLARE cur1 CURSOR FOR SELECT id FROM t1; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done1 = TRUE; <p>OPEN cur1; read1: LOOP FETCH cur1 INTO @id; IF done1 THEN LEAVE read1; END IF;</p><pre class="brush:php;toolbar:false;">BEGIN -- 新块,必须重新声明 DECLARE done2 BOOLEAN DEFAULT FALSE; DECLARE cur2 CURSOR FOR SELECT name FROM t2 WHERE pid = @id; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done2 = TRUE; OPEN cur2; ... CLOSE cur2; END;
END LOOP; CLOSE cur1; END
漏掉内层的任意一个 DECLARE,或者顺序错位,都会触发 ERROR 1337。
容易被忽略的命名冲突陷阱
游标 SELECT 的字段名,不能和接收变量名相同。比如 SELECT operation_code FROM ...,就不能用 DECLARE operation_code VARCHAR(50) 接收——MySQL 会优先解析为字段名,导致 INTO 赋值失败,且不报错,只默默跳过。
这个坑不报错,但业务逻辑就断了。务必检查:
- 游标查询字段名 ≠ 接收变量名
- 所有接收变量必须在对应
BEGIN块内DECLARE,不能靠外层传递 -
@开头的用户变量虽能跨块访问,但会破坏隔离性,别混用
真正起作用的只有显式的 BEGIN ... END 块边界,缩进、注释、空行都不影响作用域判定。










