背景:今天在处理生产上的数据写了一个存储过程,处理一个主从表的数据。最终执行结果预期与实际结果不符。最终根本原因在于游标的使用:游标在打开的时候,才会真正的执行游标定义的select语句,若在打开前游标前,存储过程中其他部分对数据进行修改,则打开游标获取的数据,是修改后的数据。
废话不多说,上代码。
- create or replace procedure EF_EARNEABILL_procedure
- as
- --游标一 实收单单头
- cursor cur1 is
- SELECT
- T.EAR_ID as EAR_ID,
- T.EAR_NO ||T.ORG_ID as EAR_NO
- FROM
- (
- SELECT
- EF.EAR_ID,
- EF.EAR_NO,
- EF.ORG_ID
- FROM
- EF_EARNEABILL EF
- LEFT JOIN ES_USER ES ON ES.ESUS_ID = EF.CREATOR
- WHERE EF.EAR_NO IN(SELECT EAR_NO FROM EF_EARNEABILL WHERE 1=1 GROUP BY EAR_NO HAVING COUNT(1)>1)
- )T
- ;
- --游标二 实收单明细
- cursor cur2 is
- SELECT
- T.EARENT_ID as EARENT_ID,
- T.EARENT_EAR_NO || T.ORG_ID as EARENT_EAR_NO
- FROM
- (
- SELECT
- EF.EARENT_ID,
- EF.EARENT_EAR_NO,
- EF.ORG_ID
- FROM
- EF_EARNEABILLENTRY EF
- LEFT JOIN ES_USER ES ON ES.ESUS_ID = EF.CREATOR
- WHERE EF.EARENT_EAR_NO IN(SELECT EAR_NO FROM EF_EARNEABILL WHERE 1=1 GROUP BY EAR_NO HAVING COUNT(1)>1)
- )T
- ;
- begin
- --遍历实收单头游标结果,后根据结果更新数据
- for cur_result1 in cur1 loop
- update EF_EARNEABILL set EAR_NO = cur_result1.EAR_NO where EAR_ID = cur_result1.EAR_ID;
- end loop;
- --遍历实收明细游标结果,后根据结果更新数据
- for cur_result2 in cur2 loop
- update EF_EARNEABILLENTRY set EARENT_EAR_NO = cur_result2.EARENT_EAR_NO where EARENT_ID = cur_result2.EARENT_ID;
- end loop;
- end EF_EARNEABILL_procedure;
大家注意看红色游标打开部分:首先打开了游标cur1,遍历游标,并根据游标获取的值更新了实收单头数据,
然后打开游标cur2,遍历游标,
获取游标值,更新实收明细明细,ok, 没毛病,执行存储过程去。
运行结果:单头数据更新正确,明细数据未变化。
分析: 明细没有变化,说明明细对应的游标cur2,没有查到相关值呗!但,我在运行存过过程之前,是有单独运行游标sql语句的,是有相关数据的,
说明什么?说明在运行存储过程当中值是有变化的,大家可以查看蓝色部分代码,此语句是获取相关的单号,游标cur1、cur2 都是使用,
这相关的单号.但是单头数据更新正确,说明蓝色部分代码查询的单号是存在的,可是明明使用的相同代码却没获取到相关单号(因为明细数据未变化),
这是为什么呢?哦,原来我们在遍历游标cur1,后此时实收单头,实收单号已经被更新,对蓝色获取相关单号语句查询结果是有影响的。此时问题已经很明朗了,
原来游标执行是打开的时候,才开始真正的去执行select,所以当我们打开cur2时候,select获取到的结果是更新后的结果,没有获取到正确的结果,
最终出现 单头数据更新正确,明细数据未变化 现象。
原因: 游标的实际运行时期为开启游标动作开始时.
问题原因找到了,修改就好办了,我们只需要将实收单明细游标操作及更新数据放在 更新单头之前,即可。
更改后代码:
- create or replace procedure EF_EARNEABILL_procedure
- as
- --游标一 实收单单头
- cursor cur1 is
- SELECT
- T.EAR_ID as EAR_ID,
- T.EAR_NO ||T.ORG_ID as EAR_NO
- FROM
- (
- SELECT
- EF.EAR_ID,
- EF.EAR_NO,
- EF.ORG_ID
- FROM
- EF_EARNEABILL EF
- LEFT JOIN ES_USER ES ON ES.ESUS_ID = EF.CREATOR
- WHERE EF.EAR_NO IN(SELECT EAR_NO FROM EF_EARNEABILL WHERE 1=1 GROUP BY EAR_NO HAVING COUNT(1)>1)
- )T
- ;
- --游标二 实收单明细
- cursor cur2 is
- SELECT
- T.EARENT_ID as EARENT_ID,
- T.EARENT_EAR_NO || T.ORG_ID as EARENT_EAR_NO
- FROM
- (
- SELECT
- EF.EARENT_ID,
- EF.EARENT_EAR_NO,
- EF.ORG_ID
- FROM
- EF_EARNEABILLENTRY EF
- LEFT JOIN ES_USER ES ON ES.ESUS_ID = EF.CREATOR
- WHERE EF.EARENT_EAR_NO IN(SELECT EAR_NO FROM EF_EARNEABILL WHERE 1=1 GROUP BY EAR_NO HAVING COUNT(1)>1)
- )T
- ;
- begin
- --遍历实收明细游标结果,后根据结果更新数据
- for cur_result2 in cur2 loop
- update EF_EARNEABILLENTRY set EARENT_EAR_NO = cur_result2.EARENT_EAR_NO where EARENT_ID = cur_result2.EARENT_ID;
- end loop;
- --遍历实收单头游标结果,后根据结果更新数据
- for cur_result1 in cur1 loop
- update EF_EARNEABILL set EAR_NO = cur_result1.EAR_NO where EAR_ID = cur_result1.EAR_ID;
- end loop;
- end EF_EARNEABILL_procedure;
经检验,运行结果正确。