通过MySQL存储过程实现双重for循环遍历结果集,使用游标获取外层结果集,在循环内根据外层变量执行内层SQL更新操作。该方法适用于按规则更新同类型数据,核心是将外层查询的oid作为参数代入内层更新语句。
最近遇到一个实际需求:需要对下面这种类型的结果集进行更新。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
更新的规则是:type为c的currentValue = (type为b的currentValue) / ((type为b的currentValue) + (type为a的currentValue)) * 100。
此类需求有多种解法。看到这个场景时,自然而然地想到了双重for循环:先查询第一个结果集,其中包含oid字段,然后遍历该结果集,将每个oid作为参数代入第二个SQL进行更新。
本文采用定义存储过程的方式来实现对结果集的遍历,即通常所说的“双重for循环”。
工具:na vicat。
数据准备:
DROP TABLE IF EXISTS `report_data`;CREATE TABLE `report_data` ( `id` int(255) NOT NULL, `oid` int(255) NOT NULL, `type` varchar(10) not NULL, `currentValue` double not NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `report_data` (`id`, `oid`, `type`, `currentValue`) VALUES (1, 1, 'a', 1);INSERT INTO `report_data` (`id`, `oid`, `type`, `currentValue`) VALUES (2, 1, 'b', 2);INSERT INTO `report_data` (`id`, `oid`, `type`, `currentValue`) VALUES (3, 1, 'c', 3);INSERT INTO `report_data` (`id`, `oid`, `type`, `currentValue`) VALUES (4, 1, 'd', 4);INSERT INTO `report_data` (`id`, `oid`, `type`, `currentValue`) VALUES (5, 2, 'a', 5);INSERT INTO `report_data` (`id`, `oid`, `type`, `currentValue`) VALUES (6, 2, 'b', 6);INSERT INTO `report_data` (`id`, `oid`, `type`, `currentValue`) VALUES (7, 2, 'c', 7);INSERT INTO `report_data` (`id`, `oid`, `type`, `currentValue`) VALUES (8, 2, 'd', 8);
先看一个通用的结果模板,便于后续理解:
CREATE PROCEDURE [存储过程名称()]BEGIN DECLARE s int DEFAULT 0; DECLARE [变量名 1 ] INT DEFAULT 0; DECLARE [变量名 2 ] VARCHAR ( 255 ); DECLARE [游标名] CURSOR FOR [包含结果集的 SQL ] DECLARE CONTINUE HANDLER FOR NOT FOUND SET s=1; OPEN [游标名]; FETCH [游标名] INTO [变量名 1 ],[变量名 2 ]; WHILE s <> 1 DO [你想操作的 SQL语句 ] FETCH [游标名] INTO [变量名 1 ],[变量名 2 ]; END WHILE;CLOSE [游标名];END;
逐条解释各个部分:
(1)CREATE PROCEDURE [存储过程名称()] 表示创建一个存储过程。假如起名为processdata,则这行是 CREATE PROCEDURE processdata()。
(2)BEGIN 和 END 标识函数体的开始和结束。
(3)
DECLARE[变量名 1 ] INT DEFAULT 0;DECLARE[变量名 2 ] VARCHAR ( 255 );
这两行定义变量,目的是将查询结果集放入变量中进行二次操作。需要注意:变量名不能与结果集中的字段名重复,例如结果集有id和name,则变量最好命名为idTemp、nameTemp。另外,变量类型需与字段类型对应。还有 DECLARE s int DEFAULT 0; 是定义循环控制变量,后面的while循环会用到它。
(4)
DECLARE [游标名] CURSOR FOR [包含结果集的 SQL ]
这行定义游标,里面存放结果集SQL。例如:
DECLARE stu CURSOR FOR select id,name from student group by id;
这样第一个结果集就出来了,下一步就是遍历它。
(5)DECLARE CONTINUE HANDLER FOR NOT FOUND SET s=1; 声明当游标遍历完后,将标志变量s置为1,作为循环结束条件。
(6)OPEN [游标名]; 打开游标。
(7)FETCH [游标名] INTO [变量名 1 ],[变量名 2 ]; 将游标当前行的数据赋给变量,顺序必须一一对应。例如:
FETCH stu INTO idTemp,nameTemp;
这里idTemp对应结果集中的id,nameTemp对应name。
(8)
WHILE s <> 1 DO....END WHILE;
这是while循环体。
(9)[你想操作的sql语句] 就是内部循环要执行的操作,比如写一个update:
update student set score='91' where id=idTemp and name = nameTemp;
这条语句会引用上方游标中的id和name值,代入条件进行更新。
(10)FETCH [游标名] INTO [变量名 1 ],[变量名 2 ]; 在循环体内再次fetch,将游标向后移动,供下一次循环使用。
定义完成后,执行存储过程,然后在na vicat的函数列表中找到刚才定义的函数执行即可。
上面的模板已经讲清了整个流程,现在直接套用来解决本文开头的需求。
CREATE PROCEDURE processData()BEGINDECLARE s int DEFAULT 0;DECLARE oidTemp int DEFAULT 20;DECLARE report CURSOR FOR SELECT oid from report_data GROUP BY oid;DECLARE CONTINUE HANDLER FOR NOT FOUND SET s=1; open report; fetch report into oidTemp; while s<>1 do SET @fenzi= (SELECT currentValue from report_data WHERE type='b' and oid =oidTemp); set @fenmu= (SELECT currentValue from report_data WHERE type='a' and oid =oidTemp) + (SELECT currentValue from report_data WHERE type='b' and oid =oidTemp); set @result = @fenzi/@fenmu *100; update report_data set currentvalue = @result WHERE oid =oidTemp and type='c'; fetch report into oidTemp; end while; close report;END;
执行后的结果:

以上就是用MySQL存储过程实现双重for循环遍历结果集的一个完整示例。核心思路是:先通过游标获取外层结果集,然后在循环内根据外层变量执行内层SQL。掌握这个模板后,类似的需求都可以快速套用。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述