Oracle游标的执行相关问题

来源:互联网 发布:linux 查看mysql版本 编辑:程序博客网 时间:2024/05/29 14:25

背景:今天在处理生产上的数据写了一个存储过程,处理一个主从表的数据。最终执行结果预期与实际结果不符。最终根本原因在于游标的使用:游标在打开的时候,才会真正的执行游标定义的select语句,若在打开前游标前,存储过程中其他部分对数据进行修改,则打开游标获取的数据,是修改后的数据。

废话不多说,上代码。

  1. create or replace procedure EF_EARNEABILL_procedure
  2. as
  3. --游标一 实收单单头
  4. cursor cur1 is
  5. SELECT
  6. T.EAR_ID as EAR_ID,
  7. T.EAR_NO ||T.ORG_ID as EAR_NO
  8. FROM
  9. (
  10. SELECT
  11. EF.EAR_ID,
  12. EF.EAR_NO,
  13. EF.ORG_ID
  14. FROM
  15. EF_EARNEABILL EF
  16. LEFT JOIN ES_USER ES ON ES.ESUS_ID = EF.CREATOR
  17. WHERE EF.EAR_NO IN(SELECT EAR_NO FROM EF_EARNEABILL WHERE 1=1 GROUP BY EAR_NO HAVING COUNT(1)>1)
  18. )T
  19. ;
  20. --游标二 实收单明细
  21. cursor cur2 is
  22. SELECT
  23. T.EARENT_ID as EARENT_ID,
  24. T.EARENT_EAR_NO || T.ORG_ID as EARENT_EAR_NO
  25. FROM
  26. (
  27. SELECT
  28. EF.EARENT_ID,
  29. EF.EARENT_EAR_NO,
  30. EF.ORG_ID
  31. FROM
  32. EF_EARNEABILLENTRY EF
  33. LEFT JOIN ES_USER ES ON ES.ESUS_ID = EF.CREATOR
  34. WHERE EF.EARENT_EAR_NO IN(SELECT EAR_NO FROM EF_EARNEABILL WHERE 1=1 GROUP BY EAR_NO HAVING COUNT(1)>1)
  35. )T
  36. ;
  37. begin
  38. --遍历实收单头游标结果,后根据结果更新数据
  39. for cur_result1 in cur1 loop
  40. update EF_EARNEABILL set EAR_NO = cur_result1.EAR_NO where EAR_ID = cur_result1.EAR_ID;
  41. end loop;
  42. --遍历实收明细游标结果,后根据结果更新数据
  43. for cur_result2 in cur2 loop
  44. update EF_EARNEABILLENTRY set EARENT_EAR_NO = cur_result2.EARENT_EAR_NO where EARENT_ID = cur_result2.EARENT_ID;
  45. end loop;
  46. end EF_EARNEABILL_procedure;
大家注意看红色游标打开部分:首先打开了游标cur1,遍历游标,并根据游标获取的值更新了实收单头数据,然后打开游标cur2,遍历游标,
获取游标值,更新实收明细明细,ok, 没毛病,执行存储过程去。
运行结果:单头数据更新正确,明细数据未变化。
分析: 明细没有变化,说明明细对应的游标cur2,没有查到相关值呗!但,我在运行存过过程之前,是有单独运行游标sql语句的,是有相关数据的,
说明什么?说明在运行存储过程当中值是有变化的,大家可以查看蓝色部分代码,此语句是获取相关的单号,游标cur1、cur2都是使用,
这相关的单号.但是单头数据更新正确,说明蓝色部分代码查询的单号是存在的,可是明明使用的相同代码却没获取到相关单号(因为明细数据未变化),
这是为什么呢?哦,原来我们在遍历游标cur1,后此时实收单头,实收单号已经被更新,对蓝色获取相关单号语句查询结果是有影响的。此时问题已经很明朗了,
原来游标执行是打开的时候,才开始真正的去执行select,所以当我们打开cur2时候,select获取到的结果是更新后的结果,没有获取到正确的结果,
最终出现单头数据更新正确,明细数据未变化 现象。
原因:游标的实际运行时期为开启游标动作开始时.


问题原因找到了,修改就好办了,我们只需要将实收单明细游标操作及更新数据放在 更新单头之前,即可。

更改后代码:
  1. create or replace procedure EF_EARNEABILL_procedure
  2. as
  3. --游标一 实收单单头
  4. cursor cur1 is
  5. SELECT
  6. T.EAR_ID as EAR_ID,
  7. T.EAR_NO ||T.ORG_ID as EAR_NO
  8. FROM
  9. (
  10. SELECT
  11. EF.EAR_ID,
  12. EF.EAR_NO,
  13. EF.ORG_ID
  14. FROM
  15. EF_EARNEABILL EF
  16. LEFT JOIN ES_USER ES ON ES.ESUS_ID = EF.CREATOR
  17. WHERE EF.EAR_NO IN(SELECT EAR_NO FROM EF_EARNEABILL WHERE 1=1 GROUP BY EAR_NO HAVING COUNT(1)>1)
  18. )T
  19. ;
  20. --游标二 实收单明细
  21. cursor cur2 is
  22. SELECT
  23. T.EARENT_ID as EARENT_ID,
  24. T.EARENT_EAR_NO || T.ORG_ID as EARENT_EAR_NO
  25. FROM
  26. (
  27. SELECT
  28. EF.EARENT_ID,
  29. EF.EARENT_EAR_NO,
  30. EF.ORG_ID
  31. FROM
  32. EF_EARNEABILLENTRY EF
  33. LEFT JOIN ES_USER ES ON ES.ESUS_ID = EF.CREATOR
  34. WHERE EF.EARENT_EAR_NO IN(SELECT EAR_NO FROM EF_EARNEABILL WHERE 1=1 GROUP BY EAR_NO HAVING COUNT(1)>1)
  35. )T
  36. ;
  37. begin
  38. --遍历实收明细游标结果,后根据结果更新数据
  39. for cur_result2 in cur2 loop
  40. update EF_EARNEABILLENTRY set EARENT_EAR_NO = cur_result2.EARENT_EAR_NO where EARENT_ID = cur_result2.EARENT_ID;
  41. end loop;
  42. --遍历实收单头游标结果,后根据结果更新数据
  43. for cur_result1 in cur1 loop
  44. update EF_EARNEABILL set EAR_NO = cur_result1.EAR_NO where EAR_ID = cur_result1.EAR_ID;
  45. end loop;
  46. end EF_EARNEABILL_procedure;
经检验,运行结果正确。

原创粉丝点击