您当前的位置: 首页 > 数据库教程 > Oracle教程 > 详解ORACLE中游标的生命周期

详解ORACLE中游标的生命周期

作者:不详 来源:网络 发布时间: 2014-08-23 20:52 点击:
在网络或者书籍中,我们可以非常容易的了解到ORACLE中游标的生命周期包括如下部分: 1,打开游标-- open cursor,此步骤在 UGA 里申请一块内存给游标使用,这个时候游标还没有与sql语句关联。 2,解析游标-- sql与游标关联起来,解析sql的内容(包括执行计划),解析后

详解ORACLE中游标的生命周期

  在网络或者书籍中,我们可以非常容易的了解到ORACLE中游标的生命周期包括如下部分:

  1,打开游标-- open cursor,此步骤在 UGA 里申请一块内存给游标使用,这个时候游标还没有与sql语句关联。

  2,解析游标-- sql与游标关联起来,解析sql的内容(包括执行计划),解析后的内容会被加载到共享池中(share pool-- library cache)。在UGA申请的内存用来保存指向这个共享游标(share cursor)在library cache中的位置。

  3,定义输出变量-- 如果sql语句返回数据,必须先定义接收数据的变量。这一点不仅对查询语句很重要,对于使用returning 自居的delete、insert和update 语句也很重要。

  4,绑定输入变量-- 如果sql语句使用了绑定变量,必须提供他们的值。绑定的过程是不做什么检查。如果指定了无效的数据,执行的过程中会爆出一个运行时错误。

  5,执行游标-- 执行跟游标关联的sql。注意 数据库并非总是在这一步做重要的事情。事实上,对于很多类型的查询语句来说,真正的处理过程通常会被推迟到fetch数据阶段。

  6,获取游标-- 如果sql语句返回数据,这一步会接受这些数据。特别是在查询语句中,大部分的处理工作都是在这一步进行的。在查询语句中,可能只会读取部分记录,换句话讲,游标有可能在取到所有记录前被关闭。

  7,关闭游标-- 释放UGA中与这个游标有关的资源,从而这些资源可供其他的游标使用。在library cache中的share cursor不会被清除,它会继续保留在library cache 中,等待被重用(软解析重用)。

  重复的内容,我们不做过多介绍,今天我们来看一下游标生命周期中各个部分所扮演的角色,以及如何利用它们来优化我们的程序。

  借鉴《oracle性能诊断艺术》中的代码片段,我们来研究一下游标的生命周期;

  ?

  

  

  

  

  

  

  

  


  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18

  19

  20

  21

  22

  23

  24

  25

  26

  27

  28

  29

  


  create or replace procedure cursor_test as

    l_ename emp.ename%TYPE := 'SCOTT';

    l_empno dbms_sql.Number_Table;

    l_cursor INTEGER;

    l_retval INTEGER;

    cnt integer := 1;

    indx integer := 1;

    res varchar2(4000);

  BEGIN

    for i in 1 .. 1000 loop

      l_cursor := DBMS_SQL.open_cursor;

      DBMS_SQL.parse(l_cursor,

                     'select empno from emp where ename <> :ename and 0 <> '||i , DBMS_SQL.native);

      l_empno.delete();

      DBMS_SQL.define_array(l_cursor, 1, l_empno,cnt,indx);

      DBMS_SQL.bind_variable(l_cursor, ':ename', to_char(i));

      l_retval := DBMS_SQL.execute(l_cursor);

      while DBMS_SQL.fetch_rows(l_cursor) > 0 loop

        dbms_sql.column_value(l_cursor, 1, l_empno);

      end loop;

      res :='';

      for j in 1 .. l_empno.count() loop

          res := res || L_EMPNO(j);

       end loop;

       DBMS_OUTPUT.PUT_LINE(res);

      dbms_sql.close_cursor(l_cursor);

    end loop;

  end;

  


  通过plsql profiler跟踪各行代码的执行效率如下:

  


  可以看出,游标生命周期中的各个部分均会占有执行时间,因此,如果可以消除一些执行步骤显然会提高性能. 那些操作可以消除那?参加下图

  ?

  

  

  

  

  

  

  

  


  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18

  19

  20

  21

  22

  23

  24

  25

  26

  27

  28

  29

  30

  31

  32

  33

  34

  35

  36

  37

  38

  39

  40

  41

  42

  43

  44

  45

  46

  47

  48

  49

  50

  51

  52

  53

  54

  55

  


  --                     -----------

    --                   | open_cursor |

    --                     -----------

    --                          |

    --                          |

    --                          v

    --                        -----

    --         ------------>| parse |

    --        |              -----

    --        |                |

    --        |                |---------

    --        |                v        |

    --        |          -------------- |

    --        |-------->| bind_variable | |

    --        |    ^    -------------  |

    --        |    |          |        |

    --        |     -----------|        |

    --        |                |<--------

    --        |                v

    --        |              query?---------- yes ---------

    --        |                |                          |

    --        |               no                          |

    --        |                |                          |

    --        |                v                          v

    --        |             -------                 -------------

    --        |----------->| execute |           ->| define_column |

    --        |             -------            |   -------------

    --        |                |------------   |         |

    --        |                |           |   ----------|

    --        |                v           |             v

    --        |          --------------    |          -------

    --        |      ->| variable_value |  | ------>| execute |

    --        |     |   --------------    | |        -------

    --        |     |         |           | |           |

    --        |      ----------|           | |           |

    --        |                |           | |           v

    --        |                |           | |       ----------

    --        |                |<----------- |----->| fetch_rows |

    --        |                |             |       ----------

    --        |                |             |           |

    --        |                |             |           v

    --        |                |             |   --------------------

    --        |                |             | | column_value        |

    --        |                |             | | variable_value      |

    --        |                |             |   ---------------------

    --        |                |             |           |

    --        |                |<--------------------------

    --        |                |

    --         -----------------|

    --                          |

    --                          v

    --                     ------------

    --                   | close_cursor |

    --                     ------------

    --

  


  首先,将游标的打开和关闭修改为只执行一次,效果如下

  


  再来将解析消除,首先清空共享池,看看游标解析是否消耗大量资源

  可以看出,解析还是消耗资源的,尤其是硬解析时,同时也可以看到open_cursor消耗的时间剧增,因此不到万不得已,千万不要清空共享池.如果要消除解析,那么每次执行的语句必须是一致的,可以通过绑带变量的方式来实现,修改程序代码如下

  ?

  

  

  

  

  

  

  

  


  1

  2

  3

  4

  5

  6

  7

  8

  9

  10

  11

  12

  13

  14

  15

  16

  17

  18

  19

  20

  21

  22

  23

  24

  25

  26

  27

  28

  29

  


  create or replace procedure cursor_test as

    l_ename emp.ename%TYPE := 'SCOTT';

    l_empno dbms_sql.Number_Table;

    l_cursor INTEGER;

    l_retval INTEGER;

    cnt integer := 1;

    indx integer := 1;

    res varchar2(4000);

  BEGIN

    l_cursor := DBMS_SQL.open_cursor;

    DBMS_SQL.parse(l_cursor,

                     'select empno from emp where ename <> :ename' , DBMS_SQL.native);

    for i in 1 .. 1000 loop

      l_empno.delete();

      DBMS_SQL.define_array(l_cursor, 1, l_empno,cnt,indx);

      DBMS_SQL.bind_variable(l_cursor, ':ename', to_char(i));

      l_retval := DBMS_SQL.execute(l_cursor);

      while DBMS_SQL.fetch_rows(l_cursor) > 0 loop

        dbms_sql.column_value(l_cursor, 1, l_empno);

      end loop;

      res :='';

      for j in 1 .. l_empno.count() loop

          res := res || L_EMPNO(j);

       end loop;

       DBMS_OUTPUT.PUT_LINE(res);

    end loop;

     dbms_sql.close_cursor(l_cursor);

  end;

  


  执行效率如下:

  


  下面我们来优化最耗时的部分,通过增加cnt的取值,(类似于SQLPLUS中的set arraysize )

  


  效率有提高了很多.

  总结:理解游标的执行过程,尽可能多的重复各个子环节可以有效提高效率.
分享到:
本文"详解ORACLE中游标的生命周期"由远航站长收集整理而来,仅供大家学习与参考使用。更多网站制作教程尽在远航站长站。
顶一下
(0)
0%
踩一下
(0)
0%
[点击 次] [返回上一页] [打印]
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 密码: 验证码:
关于本站 - 联系我们 - 网站声明 - 友情连接- 网站地图 - 站点地图 - 返回顶部
Copyright © 2007-2013 www.yhzhan.com(远航站长). All Rights Reserved .
远航站长:为中小站长提供最佳的学习与交流平台,提供网页制作与网站编程等各类网站制作教程.
官方QQ:445490277 网站群:26680406 网站备案号:豫ICP备07500620号-4