Mysql 存储过程,开启游标循环后,重复循环插入多次数据。

本文探讨了一次在使用MySQL存储过程时遇到的问题,即在游标循环中,原本应只插入一条数据的操作错误地插入了多条。问题源于Fetch游标的位置不正确,解决方案是在判断游标是否为空的IF语句之前进行Fetch操作,修正后问题得到解决。

今天在写存储过程,给其他表插入数据时,发现,我游标取出来的数据明明只有一条,但是在循环过程中,却发现插入了两条数据。下面是代码

BEGIN
	#Routine body goes here...
	DECLARE event_content1 VARCHAR(255);
	DECLARE event_id1 INT;
	DECLARE event_name VARCHAR(255);
	DECLARE start_time1 TIMESTAMP;
	DECLARE end_time1 TIMESTAMP;
	DECLARE  result_counts1 INT;
	DECLARE High_result_time1 TIMESTAMP;
	DECLARE high_result_counts1 INT;
	DECLARE start_result_time1 TIMESTAMP;
	DECLARE start_result_name1 text;
	DECLARE reposts_counts1 INT;
	DECLARE comments_counts1 INT;
	DECLARE attitude_counts1 INT;
DECLARE done INT DEFAULT FALSE;
	DECLARE  cur cursor for SELECT id,event_content,create_time,finish_time FROM t_public_concern_event WHERE `status` = 1  ;
	DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
	OPEN cur;
	TRUNCATE TABLE event_preview;
	read_loop:LOOP
    
		if done THEN
            LEAVE read_loop;
            END IF;
		# 游标 获取事件列表
		FETCH cur INTO event_id1,event_content1,start_time1,end_time1;
		
<think> 我们之前讨论了MySQL中如何将字段通过分隔符分成多列数据,现在用户转向询问MySQL存储过程循环的使用方法。根据引用内容,MySQL存储过程支持三种标准循环:WHILE、LOOP和REPEAT,以及非标准的GOTO(不推荐使用)。下面将详细讲解这三种循环结构的使用方法。 ### 1. WHILE循环 WHILE循环在满足条件时重复执行代码块,先判断条件再执行。 **语法结构**: ```sql WHILE condition DO -- 循环体 END WHILE; ``` **示例**:计算1到100的累加和 ```sql DELIMITER $$ CREATE PROCEDURE sum_while() BEGIN DECLARE i INT DEFAULT 1; DECLARE total INT DEFAULT 0; WHILE i <= 100 DO SET total = total + i; SET i = i + 1; END WHILE; SELECT total; END$$ DELIMITER ; ``` **特点**: - 先判断条件,条件为真才执行循环体 - 循环次数为0到多次 ### 2. REPEAT循环 REPEAT循环先执行一次循环体,再判断条件,条件为真则继续循环(类似do-while)。 **语法结构**: ```sql REPEAT -- 循环体 UNTIL condition END REPEAT; ``` **示例**:同样计算1到100的累加和 ```sql DELIMITER $$ CREATE PROCEDURE sum_repeat() BEGIN DECLARE i INT DEFAULT 1; DECLARE total INT DEFAULT 0; REPEAT SET total = total + i; SET i = i + 1; UNTIL i > 100 END REPEAT; SELECT total; END$$ DELIMITER ; ``` **特点**: - 至少执行一次循环体 - 条件满足时退出循环 ### 3. LOOP循环 LOOP循环是最基础的循环,需配合`LEAVE`语句退出,否则会无限循环。 **语法结构**: ```sql [loop_label:] LOOP -- 循环体 IF condition THEN LEAVE loop_label; -- 退出循环 END IF; END LOOP; ``` **示例**:计算1到100的累加和 ```sql DELIMITER $$ CREATE PROCEDURE sum_loop() BEGIN DECLARE i INT DEFAULT 1; DECLARE total INT DEFAULT 0; loop1: LOOP SET total = total + i; SET i = i + 1; IF i > 100 THEN LEAVE loop1; END IF; END LOOP; SELECT total; END$$ DELIMITER ; ``` **特点**: - 必须使用`LEAVE`显式退出循环 - 可配合标签实现嵌套循环的精确控制 ### 三种循环对比表 | **循环类型** | **执行顺序** | **退出方式** | **使用场景** | |----------------|--------------------|-----------------------|----------------------------------| | WHILE | 先判断后执行 | 条件不满足时自动退出 | 已知循环条件 | | REPEAT | 先执行后判断 | 条件满足时退出 | 至少需执行一次 | | LOOP | 无条件进入 | 需显式用LEAVE退出 | 需复杂退出逻辑或嵌套循环 | ### 使用注意事项 1. **避免无限循环**:确保循环有退出条件,特别是LOOP循环 2. **性能优化**:循环内避免复杂查询,大数据量操作优先使用集合操作 3. **变量作用域**:循环内声明的变量只在循环内有效 4. **嵌套循环**:可使用标签管理多层循环 ```sql outer: LOOP ... inner: LOOP ... IF condition THEN LEAVE outer; -- 直接退出外层循环 END IF; END LOOP; END LOOP; ``` ### 性能建议 - 超过1000次迭代的循环建议改用批量SQL操作[^2] - 在循环内执行SQL时,使用预处理语句提高效率 ```sql PREPARE stmt FROM 'INSERT INTO table VALUES(?)'; EXECUTE stmt USING @value; ``` ### 示例:循环插入数据 使用存储过程循环插入1000条测试数据: ```sql DELIMITER $$ CREATE PROCEDURE insert_data() BEGIN DECLARE i INT DEFAULT 1; WHILE i <= 1000 DO INSERT INTO test_table(id, create_time) VALUES(i, NOW()); SET i = i + 1; END WHILE; END$$ DELIMITER ; ``` > **注意**:实际批量插入建议使用单个INSERT多值语法,效率更高[^2]: > ```sql > INSERT INTO test_table (id, create_time) > VALUES (1,NOW()), (2,NOW()), ... (1000,NOW()); > ``` ### 常见问题处理 1. **中断循环**: - `LEAVE`:完全退出循环(类似break) - `ITERATE`:跳过当前迭代(类似continue) ```sql WHILE i < 10 DO SET i = i + 1; IF i MOD 2 = 0 THEN ITERATE; -- 跳过偶数 END IF; -- 奇数处理逻辑 END WHILE; ``` 2. **错误处理**: ```sql DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN -- 发生错误时执行 LEAVE loop_label; END; ``` ### 相关问题 1. 如何在存储过程中使用游标(CURSOR)遍历查询结果集? 2. 存储过程循环和应用程序中的循环哪种性能更好? 3. 如何避免存储过程循环中的锁表问题? 4. MySQL 8.0对存储过程循环有哪些性能改进? 5. 存储过程中如何调试循环逻辑? 6. 除了循环MySQL还有哪些处理重复操作的方法?[^2][^3] </think> ### MySQL存储过程循环使用方法详解 MySQL存储过程提供了三种标准的循环结构,适用于不同的业务场景: #### 1. WHILE循环(先判断后执行) ```sql DELIMITER $$ CREATE PROCEDURE while_example() BEGIN DECLARE i INT DEFAULT 1; DECLARE total INT DEFAULT 0; WHILE i <= 100 DO SET total = total + i; SET i = i + 1; END WHILE; SELECT total; -- 输出5050 END$$ DELIMITER ; ``` **特点**: - 先检查条件再执行循环体 - 适合循环次数不确定的场景 - 可能执行0到多次[^1] #### 2. REPEAT循环(先执行后判断) ```sql DELIMITER $$ CREATE PROCEDURE repeat_example() BEGIN DECLARE i INT DEFAULT 1; DECLARE total INT DEFAULT 0; REPEAT SET total = total + i; SET i = i + 1; UNTIL i > 100 END REPEAT; SELECT total; -- 输出5050 END$$ DELIMITER ; ``` **特点**: - 至少执行一次循环体 - 类似其他语言的do-while结构 - 条件满足时退出循环[^1] #### 3. LOOP循环(需显式退出) ```sql DELIMITER $$ CREATE PROCEDURE loop_example() BEGIN DECLARE i INT DEFAULT 1; DECLARE total INT DEFAULT 0; myloop: LOOP SET total = total + i; SET i = i + 1; IF i > 100 THEN LEAVE myloop; -- 必须显式退出 END IF; END LOOP; SELECT total; -- 输出5050 END$$ DELIMITER ; ``` **特点**: - 必须使用`LEAVE`显式退出 - 可通过标签管理嵌套循环 - 灵活控制退出时机[^3] --- ### 循环控制语句 | **语句** | **作用** | **示例** | |----------|-----------------------|------------------------------| | `LEAVE` | 退出循环(类似break) | `LEAVE loop_label;` | | `ITERATE`| 跳过当前迭代 | `ITERATE loop_label;` | **嵌套循环示例**: ```sql DELIMITER $$ CREATE PROCEDURE nested_loop() BEGIN DECLARE i INT DEFAULT 1; DECLARE j INT; outer: LOOP SET j = 1; inner: LOOP -- 数据处理逻辑 SET j = j + 1; IF j > 10 THEN LEAVE inner; END IF; END LOOP; SET i = i + 1; IF i > 5 THEN LEAVE outer; END IF; END LOOP; END$$ DELIMITER ; ``` --- ### 性能优化注意事项 1. **避免循环内执行SQL** ```sql -- 不推荐(性能差) WHILE i < 1000 DO INSERT INTO table VALUES(i); SET i = i + 1; END WHILE; -- 推荐(批量操作) INSERT INTO table SELECT seq FROM number_table WHERE seq BETWEEN 1 AND 1000; ``` 2. **大数据量处理原则** - 10万行以下:可使用循环 - 超过10万行:优先考虑集合操作或应用层处理[^2] 3. **变量作用域** - 使用`DECLARE`声明局部变量 - 会话变量使用`@var_name`前缀 - 避免变量名与列名冲突 --- ### 典型应用场景 1. **数据分批次处理** ```sql CREATE PROCEDURE batch_update() BEGIN DECLARE start_id INT DEFAULT 0; WHILE EXISTS(SELECT 1 FROM orders WHERE id > start_id) DO UPDATE orders SET status = 'processed' WHERE id BETWEEN start_id AND start_id + 100; SET start_id = start_id + 100; END WHILE; END ``` 2. **层级数据遍历** ```sql CREATE PROCEDURE traverse_tree(IN root_id INT) BEGIN DECLARE current_id INT; DECLARE done BOOL DEFAULT FALSE; DECLARE cur CURSOR FOR ... -- 配合游标使用 ... END ``` --- ### 常见错误解决方案 1. **无限循环预防** ```sql DECLARE max_iter INT DEFAULT 1000; DECLARE counter INT DEFAULT 0; WHILE condition AND counter < max_iter DO ... SET counter = counter + 1; END WHILE; ``` 2. **循环内事务控制** ```sql START TRANSACTION; WHILE ... DO -- 操作 IF error_occurred THEN ROLLBACK; LEAVE loop_label; END IF; END WHILE; COMMIT; ``` --- ### 相关问题 1. 如何在存储过程中高效处理百万级数据更新? 2. 存储过程循环游标(cursor)配合使用的正确方法是什么? 3. MySQL 8.0对存储过程性能有哪些改进? 4. 如何调试存储过程中的循环逻辑? 5. 存储过程循环和应用程序循环哪种效率更高?[^2] 6. 存储过程中如何实现递归查询?[^3]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值