MySQL存储过程例子,包含事务,参数,嵌套调用,游标,循环等

本文介绍了一个MySQL存储过程,该过程用于同步特定表中的数据变化。通过定义两个存储过程,分别实现了数据同步任务的初始化及具体执行逻辑。文章详细解释了如何通过游标遍历指定范围内的记录,并根据不同操作类型(插入、更新、删除)执行相应的数据同步。
drop procedure if exists pro_rep_shadow_rs;
delimiter |
----------------------------------
-- rep_shadow_rs
-- 用来处理信息的增加,更新和删除
-- 每次只更新上次以来没有做过的数据
-- 根据不同的标志位
-- 需要一个输出的参数,
-- 如果返回为0,则调用失败,事务回滚
-- 如果返回为1,调用成功,事务提交
--
-- 测试方法
-- call pro_rep_shadow_rs(@rtn);
-- select @rtn;
----------------------------------
create procedure pro_rep_shadow_rs(out rtn int)
begin
	-- 声明变量,所有的声明必须在非声明的语句前面
	declare iLast_rep_sync_id int default -1;
	declare iMax_rep_sync_id int default -1;
    -- 如果出现异常,或自动处理并rollback,但不再通知调用方了
	-- 如果希望应用获得异常,需要将下面这一句,以及启动事务和提交事务的语句全部去掉
    declare exit handler for sqlexception rollback;
    -- 查找上一次的
	select eid into iLast_rep_sync_id from rep_de_proc_log where tbl='rep_shadow_rs';
	-- 如果不存在,则增加一行
	if iLast_rep_sync_id=-1 then
	  insert into rep_de_proc_log(rid,eid,tbl) values(0,0,'rep_shadow_rs');
	  set iLast_rep_sync_id = 0;
	end if;
	
	-- 下一个数字
	set iLast_rep_sync_id=iLast_rep_sync_id+1;
	-- 设置默认的返回值为0:失败
	set rtn=0;
	
	-- 启动事务
	start transaction;
	-- 查找最大编号
	select max(rep_sync_id) into iMax_rep_sync_id from rep_shadow_rs;
	-- 有新数据
	if iMax_rep_sync_id>=iLast_rep_sync_id then
		-- 调用
		call pro_rep_shadow_rs_do(iLast_rep_sync_id,iMax_rep_sync_id);
		-- 更新日志
		update rep_de_proc_log set rid=iLast_rep_sync_id,eid=iMax_rep_sync_id where tbl='rep_shadow_rs';
	end if;
	
	-- 运行没有异常,提交事务
	commit;
	-- 设置返回值为1
	set rtn=1;
end;
|
delimiter ;
drop procedure if exists pro_rep_shadow_rs_do;
delimiter |
---------------------------------
-- 处理指定编号范围内的数据
-- 需要输入2个参数
-- last_rep_sync_id 是编号的最小值
-- max_rep_sync_id 是编号的最大值
-- 无返回值
---------------------------------
create procedure pro_rep_shadow_rs_do(last_rep_sync_id int, max_rep_sync_id int)
begin
	declare iRep_operationtype varchar(1);
	declare iRep_status varchar(1);
	declare iRep_Sync_id int;
	declare iId int;
	-- 这个用于处理游标到达最后一行的情况
	declare stop int default 0;
	-- 声明游标
	declare cur cursor for select id,Rep_operationtype,iRep_status,rep_sync_id from rep_shadow_rs where rep_sync_id between last_rep_sync_id and max_rep_sync_id;
	-- 声明游标的异常处理,设置一个终止标记
	declare CONTINUE HANDLER FOR SQLSTATE '02000' SET stop=1;
	
	-- 打开游标
	open cur;
	
	-- 读取一行数据到变量
	fetch cur into iId,iRep_operationtype,iRep_status,iRep_Sync_id;
	-- 这个就是判断是否游标已经到达了最后
	while stop <> 1 do
		-- 各种判断
		if iRep_operationtype='I' then
			insert into rs0811 (id,fnbm) select id,fnbm from rep_shadow_rs where rep_sync_id=iRep_sync_id;
		elseif iRep_operationtype='U' then
		begin
			if iRep_status='A' then
				insert into rs0811 (id,fnbm) select id,fnbm from rep_shadow_rs where rep_sync_id=iRep_sync_id;
			elseif iRep_status='B' then
				delete from rs0811 where id=iId;
			end if;
		end;
		elseif iRep_operationtype='D' then
			delete from rs0811 where id=iId;
		end if;	
		
		-- 读取下一行的数据	
		fetch cur into iId,iRep_operationtype,iRep_status,iRep_Sync_id;
	end while; 	-- 循环结束
	close cur; -- 关闭游标
 end;
|

<think>我们正在讨论MySQL游标的使用,特别是如何实现类似for循环的遍历。根据提供的引用信息,MySQL游标允许我们在查询结果集上逐行执行操作。虽然MySQL本身没有直接的FOR循环语法来处理游标,但我们可以使用LOOP、REPEAT或WHILE循环配合游标来实现类似功能。 步骤: 1. 声明游标:定义一个游标来保存查询结果。 2. 打开游标:执行查询并将结果集放入游标。 3. 循环获取数据:使用循环语句(如LOOP)和游标的FETCH语句来逐行读取数据。 4. 处理数据:对每一行数据进行处理。 5. 关闭游标:完成操作后关闭游标。 注意:使用游标前需要声明变量来存储每行提取的数据,同时还要声明一个NOT FOUND的处理程序以避免循环中的错误。 下面是一个示例存储过程,它演示了如何使用游标进行循环遍历: ```sql DELIMITER $$ CREATE PROCEDURE example_cursor_loop() BEGIN -- 声明变量用于存储每行的数据 DECLARE done INT DEFAULT FALSE; DECLARE employee_id INT; DECLARE employee_name VARCHAR(255); -- 声明游标 DECLARE cur CURSOR FOR SELECT id, name FROM employees; -- 声明当游标没有更多行时设置done为TRUE DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -- 打开游标 OPEN cur; -- 开始循环 read_loop: LOOP FETCH cur INTO employee_id, employee_name; IF done THEN LEAVE read_loop; END IF; -- 在这里处理每一行数据,例如:打印或插入到其他表 -- 这里简单演示:调用一个打印过程(假设存在)或进行其他操作 -- 比如:INSERT INTO some_table (col1, col2) VALUES (employee_id, employee_name); END LOOP; -- 关闭游标 CLOSE cur; END$$ DELIMITER ; ``` 在这个例子中: - 我们首先声明了几个变量:`done`用于标记循环结束,`employee_id`和`employee_name`用于存储从游标中提取的每一行的值。 - 声明游标`cur`,它从一个名为`employees`的表中选择`id`和`name`。 - 声明了一个CONTINUE HANDLER,当游标没有更多行可提取时(即NOT FOUND),将`done`设置为TRUE。 - 打开游标后,进入名为`read_loop`的循环。 - 在循环中,首先使用FETCH语句将当前行的数据提取到变量中。 - 然后检查`done`变量,如果为TRUE则跳出循环(使用LEAVE语句)。 - 否则,在循环体内处理这一行数据(这里只是一个示例,你可以替换为实际的操作)。 - 循环结束后关闭游标。 需要注意的是,游标在处理大量数据时可能效率较低,因为它是一行一行处理的。如果可能,尽量使用集合操作(如一条SQL语句更新多行)来代替游标循环。 另外,引用[3]提到在存储过程中实现双层循环嵌套游标)也是可能的,但同样需要注意性能问题。嵌套游标的基本思路是:在外层游标循环中打开内层游标,然后内层游标再遍历自己的结果集。 例如,双层游标循环的伪代码结构: ```sql DECLARE outer_done INT DEFAULT FALSE; DECLARE outer_id INT; DECLARE outer_cur CURSOR FOR ...; DECLARE CONTINUE HANDLER FOR NOT FOUND SET outer_done = TRUE; OPEN outer_cur; outer_loop: LOOP FETCH outer_cur INTO outer_id; IF outer_done THEN LEAVE outer_loop; END IF; -- 内部游标开始 BEGIN DECLARE inner_done INT DEFAULT FALSE; DECLARE inner_value INT; DECLARE inner_cur CURSOR FOR ...; -- 内部查询可能依赖outer_id DECLARE CONTINUE HANDLER FOR NOT FOUND SET inner_done = TRUE; OPEN inner_cur; inner_loop: LOOP FETCH inner_cur INTO inner_value; IF inner_done THEN LEAVE inner_loop; END IF; -- 处理内部游标的每一行 END LOOP; CLOSE inner_cur; END; END LOOP; CLOSE outer_cur; ``` 请根据实际需求调整。注意,为了避免变量名冲突,内部游标的逻辑放在了一个BEGIN...END块中,并声明了自己的处理程序和变量。 希望这个回答对你有所帮助。 §§相关问题§§ 1. 在MySQL存储过程中如何避免游标使用中的性能问题? 2. 除了游标MySQL还有哪些处理结果集的方法? 3. 如何在MySQL中实现嵌套游标循环(双层循环)? 4. 游标与临时表结合使用的场景有哪些? </think>以下是MySQL中使用游标实现FOR循环的详细方法,结合了引用中的关键信息: ### MySQL游标FOR循环使用步骤 1. **声明变量和游标** 需先定义存储数据的变量和游标对象: ```sql DECLARE done BOOLEAN DEFAULT FALSE; DECLARE var_id INT; DECLARE var_name VARCHAR(50); DECLARE cur CURSOR FOR SELECT id, name FROM your_table; -- 定义查询结果集[^2] ``` 2. **设置终止处理器** 声明当游标无数据时的处理逻辑: ```sql DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; ``` 3. **打开游标启动循环** 使用`OPEN`激活游标,通过`LOOP`实现循环: ```sql OPEN cur; read_loop: LOOP FETCH cur INTO var_id, var_name; -- 逐行获取数据 IF done THEN LEAVE read_loop; END IF; -- 此处添加业务逻辑(示例) INSERT INTO result_table VALUES(var_id, var_name); END LOOP; CLOSE cur; -- 必须显式关闭游标[^1] ``` ### 关键特性说明 1. **单向遍历** 游标仅支持向前遍历(不可滚动),每次`FETCH`获取下一行[^1]。 2. **只读性** 不能通过游标直接修改数据(如`UPDATE`/`DELETE`)[^1]。 3. **性能优化** - 敏感游标(默认)直接访问物理数据,速度快于不敏感游标[^1]。 - 避免在循环内执行复杂查询,可减少性能损耗[^3]。 ### 完整存储过程示例 ```sql DELIMITER $$ CREATE PROCEDURE process_data() BEGIN DECLARE done BOOLEAN DEFAULT FALSE; DECLARE v_id INT; DECLARE cur CURSOR FOR SELECT id FROM main_table; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN cur; LOOP FETCH cur INTO v_id; IF done THEN LEAVE; END IF; -- 嵌套循环示例(双层游标)[^3] INSERT INTO log_table VALUES(v_id, NOW()); END LOOP; CLOSE cur; END$$ DELIMITER ; ``` > **注意**:游标处理大量数据时效率较低,建议在无法用单一SQL实现复杂逻辑时才使用[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值