用pl/sql developer测一段pl/sql代码1. 编译我的pl/sql代码
2. 运行测试程序,得到预期结果。而且我的pl/sql代码中的dbms_output.put_line的输出也会正确显示
3. 然后在不重新编译我的pl/sql代码的前提下,重新运行测试程序(非debug方式),返回结果就不同了,而且dbms_output.put_line的输出没有显示。无论多少遍都是一样但是如果重新编译pl/sql代码或者以debug方式运行,返回值就总是一致的,而且dbms_output.put_line的输出正确显示加了PRAGMA SERIALLY_REUSABLE之后,就不会出现这个现象了,但是我还是有一点不明白,包变量是session级别的,被缓存了可以理解,但是就算是被缓存了,为什么dbms_output的输出为什么没有了呢,而且好像子函数也没有执行(直接就返回了true)。这是怎么回事啊

解决方案 »

  1.   

    1.pragma serially_reusable是包的特性。2.如果包中的某个函数或过程被调用,则包会驻留在内存中:包的全局变量会保留,像全局游标,就不会被关闭(如果一个包函数完成的功能是取表中的一个记录,则多次调用这个函数会得到不同的记录)。   但如果使包具有pragma serially_reusable特性,则可以使全局变量“不保留”,使全局游标关闭(2中说的那个函数被多次调用的话,就得到的是同一个记录)。下面这个例子,结论是:如果没有这个特性,则这两个块执行的结果一样,如果有这个特性,则结果不一样,下面那个块没有输出。declare
      i number;
      j varchar2(20);
    begin
      srPkg1.initialize;---给全局变量赋值
      i:=srPkg1.return_num;--返回全局变量
      j:=srPkg1.return_char;--返回全局变量
      dbms_output.put_line(to_char(i));
      dbms_output.put_line(j);
    end;declare
      i number;
      j varchar2(20);
    begin
      i:=srPkg1.return_num;
      j:=srPkg1.return_char;
      dbms_output.put_line(to_char(i));
      dbms_output.put_line(j);
    end;这就说明了pragma serially_reusable对包全局变量,游标的影响。
      

  2.   

    多谢,不过还是有疑问
    你给出的这个例子,我是这么理解的,如果没有pragma serially_reusable的话,那么只要运行过第一个,那么包srPkg1的全局变量就被初始化了,那么以后就算是运行第二个(即没有调用srPkg1.initialize)也会有输出,这我可以理解,因为包全局变量是session级别的。举个例子吧
    create or replace package body testSerially_reusable is
      type type_array is table of varchar(20);
      records type_array;  procedure initialize is
      begin
        records.extend;
        records(records.last) := to_char(records.last);
      end;  function return_num return number is
      begin
        return records.count;
      end;  function checknum return boolean is
        num number;
      begin
        for i in records.first .. records.last loop
          num := to_number(records(i));
          if num >= 1 then
            return false;
          end if;
          return true;
        end loop;
      end;  function return_char return varchar2 is
        total varchar2(2000) := '';
      begin
        if checknum then
          for i in records.first .. records.last loop
            total := total || records(i) || '   ';
          end loop;
          return total;
        else
          return 'too much';
        end if;
      end;begin
      if records is null then
        records := type_array();
      else
        records.delete;
      end if;
    end testSerially_reusable;上面这个函数,我在return_char首先调用了checknum,由于num >= 1的判断条件,第一运行就会输出
    1
    more than 5
    以后再运行,就一次输出
    2
    more than 5
    3
    more than 5
    等等如果我把判断条件换成num >= 3,那么问题就出现了
    连续运行的结果是
    1
    1   2
    1   2   3
    1   2   3   4
    1   2   3   4   5
    1   2   3   4   5   
    等等
    也就是说看起来就是checknum总是返回true,难道第一次checknum运行的结果被缓存了?
      

  3.   

    SERIALLY_REUSABLE, 这个PRAGMA在字面上并不能一下知道它的作用,最起码我没有一下子就知道它的意思,呵呵。     这个指令指出程序包的状态只在第一次与服务器CALL时需要,这个CALL结束后,有关这个程序包的变量的存储可以被重用,这样减少了那些需要长时间运行的会话对内存的过载。     语法,PRAGMA SERIALLY_REUSABLE;     这个编译指令对那些只用到一次的大型的临时工作区的声明并且在同一个会话接下来的数据库调用中不会再用到的程序包适用。     你也可以在程序包体标记这个编译指令,如果一个程序包包括定义及包体的话,两边都要指定这个指令,不能单指一边。     对于拥有这个编译指令的包,Oracle在SGA里为它分配内存,不在UGA里分配,这样,这个程序包的工作区可以被重用,这样就起到了这个编译指令的目的。当程序的调用结束,在SGA分配的相应的内存块返回给内存池。每次这个程序包调用时,它的公共变量初始化为它的默认值或者为NULL。     SERIALLY REUSABLE的程序包不能被数据库触发器或者在SQL语句级别对PL/SQL子程序的调用时访问,不然,Oracle产生相应的错误。     总而言之,这个编译指令主要是出于保护内存考虑,减少不必要的内存负载,充分解了那些长时间运行的会话对内存的占用
      

  4.   

    SERIALLY_REUSABLE就是重置这些数据否则只有结束当前会话的时候才会被重置