MySQL存储过程游标遍历DISTINCT结果遗漏最后一条数据问题咨询
MySQL存储过程游标遍历漏最后一条数据修复方案
问题根因
- 你定义的
CONTINUE HANDLER FOR NOT FOUND会响应所有查询的无结果状态,并非仅作用于游标FETCH操作:当循环内部的SELECT ... INTO语句查询不到匹配数据时,也会触发NOT FOUND将bDone设为1,直接提前终止循环,导致后续未处理的游标数据(包括最后一条)被跳过。 - 现有REPEAT循环逻辑存在时序缺陷:游标FETCH失败触发
bDone=1后,仍会执行一次后续处理逻辑,要么重复处理最后一条数据,要么用上一轮的旧变量值执行错误逻辑。 - 会话变量
@A/@B/@C未做清空处理,上一轮循环的旧值会干扰本轮判断,也可能导致逻辑异常提前终止。
修复后代码
CREATE PROCEDURE `my_procedure`() BEGIN DECLARE bDone INT DEFAULT 0; DECLARE Var2 INT; DECLARE curs CURSOR FOR SELECT DISTINCT(id) FROM my_table WHERE deleted = 0 and status = 1; DECLARE CONTINUE HANDLER FOR NOT FOUND SET bDone = 1; OPEN curs; -- 首次预取数据 FETCH curs INTO Var2; -- 用WHILE循环替代REPEAT,先判断游标状态再处理 WHILE bDone = 0 DO -- 清空上一轮会话变量值,避免干扰 SET @A = NULL, @B = NULL, @C = NULL; -- 用聚合函数保证无数据时返回NULL,不触发NOT FOUND SELECT MAX(PR.some_value) INTO @A FROM my_table2 PR WHERE PR.id = Var2 AND PR.status = 1 AND PR.deleted = 0; SELECT MAX(PB.some_value) INTO @B FROM my_table3 PB WHERE PB.id = Var2 AND PB.status = 1 AND PR.deleted = 0; SELECT MAX(MP.id) INTO @C FROM my_table4 MP WHERE MP.id = Var2 AND MP.deleted = 0; IF @A IS NOT NULL THEN IF @C IS NOT NULL THEN UPDATE my_table4 SET price = @A, modified = now() WHERE id = Var2; ELSE INSERT INTO my_table4 (id, price) VALUES (Var2, @A); END IF; ELSEIF @B IS NOT NULL THEN IF @C IS NOT NULL THEN UPDATE my_table4 SET price = @B, modified = now() WHERE id = Var2; ELSE INSERT INTO my_table4 (id, price) VALUES (Var2, @B); END IF; END IF; -- 处理完当前数据后取下一条 FETCH curs INTO Var2; END WHILE; CLOSE curs; END
关键修改说明
- 改用
WHILE循环逻辑,先预取游标数据,确认有有效数据再执行处理逻辑,避免FETCH失败后的无效执行 - 内部查询增加
MAX()聚合函数,无匹配数据时返回NULL不会触发NOT FOUND,不会误改游标结束标记bDone - 每次循环开始前清空会话变量,避免上一轮的残留值导致逻辑判断错误
- 改用
IS NOT NULL判断变量状态,比直接IF 变量的兼容性更强,避免值为0时被误判为无效
内容的提问来源于stack exchange,提问作者Aljaz
相关产品推荐
相关产品推荐

