MySQL_Procedure_Function_Cursor

本文详细介绍了SQL中的几种关键操作:声明、设置、选择赋值、游标、条件判断、循环与迭代、重复执行及循环控制,通过创建多个存储过程展示了如何在数据库中执行复杂的数据操作。

    1. Declare

delimiter ||
crate procedure usePro()
begin
declare a, b varchar(20);
insert into TesFun2 values(null, a, b);
end ||
delimiter;

211313_VSsV_2363350.png

call usePro();

211405_GbWt_2363350.png

   

    2. Set

delimiter ||
create procedure usePro2()
begin
declare a, b varchar(20);
set a='Linux C编程', b='123456';
insert into TesFun2 values(null, a, b);
end ||
delimiter;

211700_u5S3_2363350.png

call usePro2();

211725_dXOw_2363350.png


    3. Select into

211822_V3wf_2363350.png

delimiter ||
create procedure usePro3()
begin
declare a, b varchar(20);
select useName2, password2 into a, b from TesFun3 where id2=1;
insert into TesFun2 values(null, a, b);
end ||
delimiter;

212127_Hyj2_2363350.png

call usePro3();

212206_mkOk_2363350.png


    4. Cursor

212312_lfb3_2363350.png

212533_sBM5_2363350.png

delimiter||
create procedure usePro4()
begin
declare a, b varchar(20);
declare cur4 cursor for select useName2, password2 from TesFun3;
open cur4;
fetch cur4 into a, b;
insert into TesFun2 values(null, a, b);
close cur4;
end ||
delimiter;

212921_04t4_2363350.png

call usePro4();

212958_bzJL_2363350.png


    5. If

213411_Nggx_2363350.png

delimiter ||
create procedure tesPro5(in bookId int)
begin
select count(*) into @num from TesFun2 where id=bookId;
if @num>0 then update TesFun2 set useNam='Java12345' where id=bookId;
else
insert into TesFun2 values(null, '禾博士', 'heboshi');
end if;
end ||
delimiter;

213911_Iu9B_2363350.png

call tesPro5(4);

214007_2aia_2363350.png

call tesPro5(5);

214047_aSLs_2363350.png


    6. Case

214203_boJT_2363350.png

delimiter ||
create procedure tesPro6(in bookId int)
begin
select count(*) into @num from TesFun2 where id=bookId;
case @num
when 1 then update TesFun2 set useNam='Java1234567' where id=bookId;
when 0 then insert into TesFun2 values(null, '禾博士2', 'heboshi2');
else insert into TesFun2 values(null, '禾博士3', 'heboshi3');
end case;
end ||
delimiter;

214713_Kgfh_2363350.png

call tesPro6(3);

214746_BKMj_2363350.png

call tesPro6(8);

214837_dOVW_2363350.png


    7. Loop

214928_bQDd_2363350.png

delimiter ||
create procedure tesPro7(in totNum int)
begin
loo: loop
set totNum=totNum-1;
if totNum=0 then leave loo;
else
insert into TesFun3 values(totNum, '禾博士', 'heboshi');
end if;
end loop loo;
end ||
delimiter;

215430_uAIQ_2363350.png

call tesPro7(10);

215520_w10i_2363350.png


    8. Iterate

215608_QIWf_2363350.png

delimiter ||
create procedure tesPro8(in totNum int)
begin
loo: loop
set totNum=totNum-1;
if totNum=0 then leave loo;
elseif totNum=3 then iterate loo;
else
insert into TesFun3 values(totNum, '好玩的Java', 'mima');
end if;
end loop loo;
end ||
delimiter;

215941_s6Jq_2363350.png

call tesPro8(11);

220040_jvQD_2363350.png


    9. Repeat

220134_NejM_2363350.png

delimiter ||
create procedure tesPro9(in totNum int)
begin
rea: repeat
set totNum=totNum-1;
insert into TesFun3 values(totNum, '加油', 'jiayou');
until totNum=1;
end repeat rea;
end ||
delimiter;

220432_4i3j_2363350.png

call tesPro9(11);

220522_bn68_2363350.png



    10. While

220558_jhed_2363350.png

delimiter ||
create procedure tesPro10(in totNum int)
begin
whi: while totNum>0 do
insert into TesFun3 values(totNum, 'while', 'pwhile');
set totNum=totNum-1;
end while whi;
end ||
delimiter;

221144_XDfO_2363350.png

call tesPro10(5);

221233_iBNZ_2363350.png

转载于:https://my.oschina.net/u/2363350/blog/672476

<think>我们遇到了一个MySQL错误:在存储函数或触发器中尝试更新表't_partner_figure'时,由于该表已经被调用语句使用,导致更新失败。错误信息为:"Can't update table 't_partner_figure' in stored function/trigger because it is already used by the statement which invoked this function/trigger." ### 问题分析 在MySQL中,存储函数和触发器有一些限制,其中之一是:不能在存储函数或触发器中修改已经被调用该函数或触发器的语句(如INSERT、UPDATE、DELETE)使用过的表。这是为了防止数据不一致和无限递归等问题。 ### 解决方案 #### 方法1:重构SQL语句,避免在触发器或函数中更新同一表 如果可能,尝试将更新逻辑移到存储过程或应用程序层中,而不是在触发器或函数中执行。这样可以避免MySQL的限制。 #### 方法2:使用临时表 在触发器中,我们可以使用临时表来存储需要更新的数据,然后在触发器的后续部分使用这些临时表的数据。但注意,临时表只在当前会话中存在,不会与调用语句冲突。 #### 方法3:使用存储过程代替触发器 如果业务逻辑允许,可以考虑使用存储过程来封装整个操作,从而避免触发器的使用。在存储过程中,我们可以控制事务,并按照顺序执行多个SQL语句,包括更新同一张表的操作。 #### 方法4:使用事件调度器(如果适用) 如果更新操作可以延迟执行,可以考虑使用MySQL事件调度器。这样,在触发器中只需要将需要更新的信息记录到另一个表中,然后由事件调度器定期执行更新操作。 ### 示例:使用临时表解决 假设我们有一个触发器,在`t_partner_figure`表插入后需要更新同一张表。我们可以使用临时表来存储需要更新的记录的主键,然后在触发器的后续部分使用这些主键来执行更新。 ```sql DELIMITER // CREATE TRIGGER after_insert_partner_figure AFTER INSERT ON t_partner_figure FOR EACH ROW BEGIN -- 创建一个临时表来存储需要更新的记录ID DROP TEMPORARY TABLE IF EXISTS temp_update_records; CREATE TEMPORARY TABLE temp_update_records (id INT); -- 将需要更新的记录ID插入临时表 INSERT INTO temp_update_records (id) SELECT partner_id FROM t_partner_figure WHERE ...; -- 你的条件 -- 使用临时表的数据来更新原表 UPDATE t_partner_figure t JOIN temp_update_records tmp ON t.partner_id = tmp.id SET ...; -- 更新操作 -- 临时表会在会话结束时自动删除,也可以显式删除 DROP TEMPORARY TABLE IF EXISTS temp_update_records; END // DELIMITER ; ``` 注意:临时表的使用需要谨慎,因为临时表的作用域是当前会话,不会与其他会话冲突。 ### 注意事项 1. 在触发器中更新同一张表可能会导致性能问题,尤其是在数据量大的情况下。 2. 如果业务逻辑复杂,建议将更新操作放在应用程序中处理,而不是依赖触发器。 3. 使用临时表时,确保临时表的名字是唯一的,避免与其他临时表冲突。 ### 替代方案:使用存储过程 如果触发器中的更新逻辑无法避免,可以考虑将整个操作封装在存储过程中,然后在应用程序中调用存储过程。这样,在存储过程中,我们可以先执行插入操作,然后再执行更新操作,从而避免触发器的使用。 ```sql DELIMITER // CREATE PROCEDURE InsertAndUpdatePartnerFigure( IN p_partner_id INT, ... -- 其他参数 ) BEGIN -- 执行插入 INSERT INTO t_partner_figure (partner_id, ...) VALUES (p_partner_id, ...); -- 执行更新 UPDATE t_partner_figure SET ... WHERE partner_id = p_partner_id; END // DELIMITER ; ``` ### 总结 在MySQL中,由于限制,不能在触发器或存储函数中更新已经被调用语句使用的表。我们可以通过临时表、存储过程或事件调度器等方法来解决这个问题。具体选择哪种方法取决于业务需求和应用场景。 ### 相关问题 1. 如何在MySQL中创建和使用临时表? 2. 存储过程和触发器的区别是什么? 3. 如何避免MySQL触发器中的递归调用? 4. 如何在MySQL中使用事件调度器执行定时任务?</think>### 解决 MySQL 更新错误:Can't update table in stored function/trigger 这个错误是由于 MySQL 的**触发器/函数执行限制**导致的:当你在存储函数或触发器中尝试更新一个已被调用语句使用的表时,MySQL 会阻止这种操作以防止数据不一致和无限递归。 #### 问题原因分析 1. **表冲突**:在触发器/函数中更新`t_partner_figure`表时,该表已被主 SQL 语句(如`UPDATE`/`INSERT`)锁定 2. **递归风险**:直接更新同一表可能触发新的触发器调用,导致无限循环 3. **事务隔离**:MySQL 需要确保事务内数据一致性,禁止这种"自修改"操作 #### 解决方案 ##### 方法 1:使用临时表作为中间层(推荐) ```sql -- 创建临时表存储需要更新的ID CREATE TEMPORARY TABLE temp_update_ids AS SELECT partner_id FROM t_partners WHERE EXISTS (...); -- 原EXISTS子句条件 -- 通过临时表执行更新 UPDATE t_partners p JOIN temp_update_ids tmp ON p.partner_id = tmp.partner_id SET p.partner_type = ?; ``` ##### 方法 2:重构为存储过程 + 游标处理 ```sql DELIMITER // CREATE PROCEDURE update_partner_type() BEGIN DECLARE done INT DEFAULT FALSE; DECLARE cur_id INT; -- 声明游标获取需要更新的ID DECLARE cur CURSOR FOR SELECT partner_id FROM t_partners WHERE EXISTS (...); -- 原条件 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN cur; read_loop: LOOP FETCH cur INTO cur_id; IF done THEN LEAVE read_loop; END IF; -- 逐条更新(避免表锁冲突) UPDATE t_partners SET partner_type = ? WHERE partner_id = cur_id; END LOOP; CLOSE cur; END // DELIMITER ; -- 调用存储过程 CALL update_partner_type(); ``` ##### 方法 3:使用多级子查询绕过限制 ```sql UPDATE t_partners SET partner_type = ? WHERE partner_id IN ( SELECT partner_id FROM ( SELECT partner_id FROM t_partners WHERE EXISTS (...) ) AS tmp -- 必须使用临时表别名 ); ``` #### 关键注意事项 1. **临时表必要性**:所有子查询解决方案必须包含中间临时表(如`AS tmp`) 2. **事务控制**:在存储过程中添加`START TRANSACTION`和`COMMIT` 3. **索引优化**:确保`partner_id`字段有索引 4. **批量更新限制**:单次更新超过 1000 行建议分批次执行 #### 设计建议 ```mermaid graph TD A[主业务操作] --> B{需要更新关联表?} B -->|是| C[调用存储过程] B -->|否| D[正常执行] C --> E[创建临时表存储ID] E --> F[基于临时表执行更新] F --> G[清理临时资源] ``` > **最佳实践**:对于高频更新场景,建议将业务逻辑移至应用层(如Java/Python)处理,避免数据库层复杂操作[^1]。 ### 相关问题 1. MySQL 存储过程中如何安全地更新被锁定的表? 2. 如何优化使用临时表的批量更新性能? 3. MySQL 触发器的使用限制有哪些? 4. 存储过程与触发器的适用场景如何选择? 5. 如何避免数据库更新操作的递归调用问题?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值