MySQL存储过程要求DECLARE语句严格按“变量→游标→条件→处理程序”顺序声明,因解析器在编译阶段即校验该顺序,错序将直接报ERROR 1337;游标FETCH必须写入已声明的局部变量,且HANDLER须紧邻对应游标声明后以确保正确绑定。

MySQL解析器强制要求DECLARE顺序
因为MySQL存储过程的语法解析器在编译阶段就按固定顺序校验语句块,DECLARE语句必须严格遵循「变量 → 游标 → 条件 → 处理程序」的声明序列。一旦游标出现在变量之前,解析器会立即报错 ERROR 1337 (42000): Variable or condition declaration after cursor or handler declaration,根本不会进入执行阶段。
游标依赖已声明的变量接收数据
游标本身不存数据,FETCH时必须把结果写入已有定义的局部变量。这些变量必须在FETCH ... INTO前就存在,否则MySQL无法绑定目标地址。比如:
DECLARE cur CURSOR FOR SELECT id FROM users; FETCH cur INTO @id; -- 错!@id是用户变量,但局部变量v_id还没声明 DECLARE v_id INT; -- 这行必须在DECLARE cur之前 FETCH cur INTO v_id; -- 对
-
INTO后的变量名必须是DECLARE定义的局部变量,不能是未声明的标识符或同名字段 - 字段名和接收变量名若相同(如查询
SELECT name却用DECLARE name VARCHAR(50)),会导致赋值失败且静默为NULL - 多个字段FETCH时,变量个数、顺序、类型必须与SELECT列完全一致,否则隐式转换可能截断或置空
嵌套BEGIN END块不改变顶层声明顺序规则
即使你用BLOCK1: BEGIN ... END包一层,块内仍要遵守「先变量、再游标、后HANDLER」。有人误以为缩进或注释能绕过顺序检查——不行,只有显式BEGIN ... END能隔离作用域,但每一块内部的DECLARE顺序约束依然生效。
典型翻车点:
- 在
DECLARE CONTINUE HANDLER之后补写DECLARE cur CURSOR→ 直接语法错误 - 把
SET v_x = 1;写在DECLARE cur前面 → 解析器认为变量声明已结束,游标声明位置非法 - 试图用
IF或SELECT ... INTO提前初始化变量,再声明游标 → 不行,所有DECLARE必须堆在块最开头
为什么NOT FOUND HANDLER必须紧接游标声明之后
MySQL将CONTINUE HANDLER FOR NOT FOUND和它前面最近的游标绑定,不是全局生效。如果中间插了另一个DECLARE(哪怕只是DECLARE tmp INT),HANDLER就会失效或绑定错位,导致循环无法退出。
正确写法必须是:
DECLARE done INT DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT id FROM t; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
少一个分号、多一行空行、换行缩进不同,都不影响;但只要DECLARE cur和DECLARE CONTINUE HANDLER之间夹了其他DECLARE,就会出问题。这个细节极容易被忽略,尤其当变量多、逻辑长时。


















