PL/SQL詳細介紹


PL/SQL筆記
PL/SQL塊中只能直接嵌入SELECT,DML(INSERT,UPDATE,DELETE)以及事務控制語句(COMMIT,ROLLBACK,SAVEPOINT),而不能直接嵌入DDL語句(CREATE,ALTER,DROP)和DCL語句(GRANT,REVOKE)

1.檢索單行數據
  
  1.1使用標量變量接受數據
  v_ename emp.ename%type;
  v_sal   emp.sal%type;
  select ename,sal into v_ename,v_sal from emp where empno=&no;

  1.2使用記錄變量接受數據
  type emp_record_type is record(
  ename emp.ename%type,sal emp.sal%type);
  emp_record emp_record_type;
  select ename,sal into emp_record from emp where empno=&no;

  1.3嵌入SELECT語句注意事項:
  使用SELECT INTO語句時,必須要返回一條數據,並且只能返回一條數據
  
  no_date_found:
  select into沒有返回數據
  too_many_rows:
  select into返回多條數據
 
  where子句使用注意事項:
  使用的變量名不能與列名相同,否則觸發TOO_MANY_ROWS例外.
 

2.操縱數據
  2.1使用VALUES子句插入數據
  v_deptno dept.deptno%type;
  v_dname dept.dname%type;
  v_deptno:=no;
  v_dname:='&name';
  insert into dept (deptno,dname) values(v_deptno,v_dname);

  2.2使用子查詢插入數據
  v_deptno emp.deptno%type:=&no;
  insert into employee select * from emp where deptno=v_deptno;
 
  2.3更新數據
  使用表達式更新列值
  v_deptno dept.deptno%type:=no;
  v_loc dept.loc%type:='&loc';
  update dept set loc=v_loc where deptno=v_deptno;

  2.4使用子查詢更新列值
  v_ename emp.ename%type:='&name';
  update emp set (sal,comm) = (select sal,comm from emp where ename=v_ename) where job = (select job from emp where ename=v_ename)

  2.5刪除數據
  使用變量刪除數據
  v_deptno dept.deptno%type:=&no;
  delete from dept where deptno=v_deptno;
  
  2.6使用子查詢刪除數據
  v_ename emp.ename%type:='&name';
  delete from emp where deptno=(select deptno from emp where ename=v_ename);

  
3.SQL游標
  游標是指向上下文區的指針,包括隱含游標(SQL游標)和顯式游標兩種類型
  SQL游標用於處理SELECT INTO ,INSERT,UPDATE以及DELETE語句.
  顯式游標用於處理多行的SELECT語句
  SQL游標包括:SQL%FOUND,SQL%NOTFOUND,SQL%ROWCOUNT,SQL%ISOPEN等四種屬性
  
  3.1 SQL%ISOPEN:執行時,會隱含的打開和關閉游標.因此該屬性的值永遠都是FALSE
  
  3.2 SQL%FOUND:用於確定SQL語句執行是否成功.當SQL有作用行時,為TRUE,否則為FALSE
  v_deptno emp.deptno%type:=&no;
  update emp set sal=sal*1.1 where deptno=v_deptno;
  if sql%found then dbms_output.put_line('執行成功');  else dbms_output.putline('失敗'); endif
  
  3.3 sql%notfound:確定SQL語句執行是否成功,當SQL有作用行時,為false,否則為true
  
  3.4 sql%rowcount:返回SQL語句所作用的總計行數
  v_deptno emp.deptno%type:=&no;
  update emp set sal=sal*1.1 where deptno=v_deptno;
  dbms_output.put_line('修改了'||sql%rowcount||'行');


4.事務控制語句
  事務控制語句包括COMMIT,ROLLBACK以及SAVEPOINT等三種語句
  
  v_sal emp.sal%type:=&salary;
  v_ename emp.ename%type:='&name';
  update emp set sal=v_sal where ename=v_ename;
  commit;
 exception
    when others then
      rollback;

  insert into temp values(1);
  savepoint a1;
  insert into temp values(2);
  savepoint a2;
  insert into temp values(3);
  savepoint a3;
  rollback to a2;
  commit;


5.控制結構
  條件分支語句
 
  5.1簡單條件判斷
  v_sal number(6,2);
  select sal into v_sal from emp where lower(ename)=lowe('&&name');
  if v_sal<2000 then update emp set sal=v_sal+200 where lower(ename)=lower('&name')
  end if

  5.2二重條件分支
  v_comm number(6,2);
  select comm into v_comm from emp where empno=&&no;
  if v_comm<>0 then update emp set comm=v_comm+100 where empno=&no;
  else update emp set comm=200 where empno=&no;
  end if

  5.3多重條件分支
  v_job varchar2(10);
  v_sal number(6,2);
  select job,sal into v_job,v_sal from emp where empno=&&no;
  if v_job='president' then update emp set sal=v_sal+1000 where empno=&no;
  elseif v_job='manager' then update emp set sal=v_sal+500 where empno=&no;
  else update emp set sal=v_sal+200 where empno=&no;
  end if;


  5.4 CASE語句:
  在CASE語句中使用單一選擇符進行等值比較
  declare
   v_deptno emp deptno%type;
  begin
   v_deptno:=&no;
   case v_deptno
        when 10 then update emp set comm=100 where deptno=v_deptno;
        when 20 then update emp set comm=80  where deptno=v_deptno;
        when 30 then update emp set comm=50  where deptno=v_deptno;
   else
        dbms_output.put_line("不存在');
   end case;
   end;

   5.5 在CASE語句中使用多種條件比較
   declare
     v_sal emp.sal%type;
     v_ename emp.ename%type;
   begin
     select ename,sal into v_ename,v_sal from emp where empno=&no;
     case
       when v_sal<1000 then update emp set comm=100 where ename=v_ename;
       when v_sal<2000 then update emp set comm=80  where ename=v_ename;
       when v_sal<6000 tehn update emp set comm=50  where ename=v_ename;
     end case;
   end;


   5.6循環語句
   有基本循環,WHILE循環,FOR循環
   
   基本循環:一定要包含EXIT語句,定義循環控制變量
   create table temp(cola int);
   declare
     i int:=1;
   begin
     loop
       insert into temp values(i);
       exit when i=10;
        i:=i+1;
     end loop;
  end;

  5.7 WHILE循環:定義循環控制變量,並在循環體內改變循環控制變量的值
  declare
    i int:=1;
  begin 
    while i<=10 loop
      insert into temp values(i);
      i:=i+1;
    end loop;
  end;

  5.8 for循環:使用FOR循環時,ORACLE會隱含定義循環控制變量.
  for counter in[reverse]
  lower_bound..upper_bound loop
    statement1;
    statement2;
    .......
  end loop;
  5.9 counter是循環控制變量,並且該變量由ORACLE隱含定義,不需要顯示定義;lower_bound和upper_bound分別對應循環控制變量的上下界值.默認情況下,FOR循環,每次會自動增一,指定REVERSE選項時,每次循環控制變量會減一
  begin
    for i in reverse 1..10 loop
      insert into temp values(i);
    end loop;
  end;

 
  5.10嵌套循環和標號:通過在嵌套循環中使用標號,可以區分內層循環和外層循環,並且可以在內層循環中直接退出外層循環
  declare
    result int;
    begin
    <<outer>>
    for i in 1..100 loop
    <<inter>>
    for j in 1..100 loop
    result:=i*j;
    exit outer when result=1000;
    exit when result=500;
    end loop inner;
    dbms_ouput.put_line(result);
    end loop outer;
    dbms_output.put_line(result);
    end;

    
6.順序控制語句
  PL/SQL不僅提供了條件分支語句和循環控制語句,而且還提供了順序控制語句GOTO和NULL.一般情況下不使用
   
  6.1 GOTO:用於跳轉到特定標號處去執行語句.  
  GOTO LABEL_NAME;
  
  declare
    i int :=1;
  begin
    loop
      insert into temp values(i);
      if i=10 then
         goto end_loop
      end if;
      i:=i+1;
   end loop;
   <<end_loop>>
   dbms_output.put_line('循環結束');
   end;

  
   6.2 null:不會執行任何操作,並且會直接將控制傳遞到下一條語句.
   declare
     v_sal emp.sal%type;
     v_ename emp.ename%type;
   begin
     select ename,sal into v_ename,v_sal from emp where empno=&no;
     if v_sal<3000 then update emp set comm=sal*0.1 where ename=v_ename;
     else 
       null;
     end if;
   end;

   
7.復合數據類型
  7.1定義PL/SQL記錄
  
  自定義PL/SQL記錄:需要定義PL/SQL記錄類型和記錄變量,identifier用於指定記錄變量名

  type type_name is record(
       field_declaration[,
       field_declaration]...
  );
  identifier type_name;
  
  declare
    type emp_record_type is record(
         name emp.ename%type,
         salary em.sal%type,
         dno emp.deptno%type);
    emp_record emp_record_type;


    使用%rowtype屬性定義記錄變量:可以基於表或視圖定義記錄變量
    當使用%ROWTYPE屬性定義記錄變量時,記錄成員個數,名稱,類型與表或視圖列的個數,名稱,類型完全相同.
    dept_record dept%rowtype;
    emp_record emp%rowtype;

   
    在select into 語句中使用PL/SQL 記錄
    在select into 語句中使用記錄變量
    set serveroutput on
    declare
      type emp_record_type is record(
           name emp.ename%type,
           salary em.sal%type,
           dno emp.deptno%type);
      emp_record emp_record_type;
    begin
      select ename,sal,deptno into emp_record from emp where empno=&no;
      dbms_output.put_line(emp_record.name);
    end;

    7.2在select into 語句中使用記錄成員
    declare 
      type emp_record_type is record(
           name emp.ename%type,
           salary emp.sal%type,
           dna emp.deptno%type);
      emp_record emp_record_type;
    begin
      select ename,sal into emp_record.name,emp_record.salary from emp where empno=&no;
      dbms_output.put_line(emp_record.name);
    end;


    7.3在insert語句中使用PL/SQL記錄
    在VALUES子句中使用記錄變量
    declare 
      dept_record dept%rowtype;
    begin
      dept_record.deptno:=50;
      dept_record.dname:='administrator';
      dept_record.loc:='beijing';
      insert int dept values dept_record;
    end;

    在VALUES子句中使用記錄成員
    declare
      dept_record dept%rowtype;
    begin
      dept_record.deptno:=60;
      dept_record.dname:='sales';
      insert into dept (deptno,dname) values (dept_record.deptno,dept_record.dname);
    end;

    7.4在UPDATE語句中使用PL/SQL記錄
    在SET子句中使用記錄變量
    declare
      dept_record dept%rowtype;
    begin
      dept_record.deptno:=30;
      dept_record.dnama:='sales';
      dept_record.loc:='shanghai';
      update dept set row=dept_record where deptno=30;
   end;

   在SET子句中使用記錄成員
   declare 
     dept_record dept%rowtype;
   begin
     dept_record.loc:='guangzhou';
     update dept set loc=dept_record.loc where deptno=10;
   end;


  
   7.5在DELETE語句中使用PL/SQL記錄:只能在DELETE語句中的WHERE子句中使用記錄成員
   declare
     dept_record dept%rowtype;
   begin
     dept_record.deptno:=50;
     delete from dept where deptno=dept_record.deptno;
   end;


 
8.pl/sql集合
  處理單行單列數據,可以使用標量變量,處理單行多列的數據,可以使用PL/SQL記錄
  處理單列多行數據,可以使用PL/SQL集合
  
  PL/SQL集合類型類似於高級語言數組的一種復合數據類型
  包括:索引表(PL/SQL表),嵌套表(NESTED TABLE),變長數組(VARRAY)三種
  
  8.1 索引表:PL/SQL表 元素個數沒有限制,並且下標可以是負值
  定義索引表:
      type type_name is table of element_type [not null] index by key_type;
      identifier type_name;
  
  在索引表中使用BINARY_INTEGER和PLS_INTEGER
  set serveroutput on
  declare
    type ename_table_type is table of emp.ename%type index by binary_integer;
    ename_table ename_table_type;
  begin
    select ename into ename_table(-1) from emp where empno=&no;
    dbms_output.put_line('雇員名:'||ename_table(-1));
  end;

  在索引表中使用VARCHAR2
  set serveroutput on
  declare 
    type area_table_type is table of number index by varchar2(10);
    area_table area_table_type;
  begin
    area_table('北京'):=1;
    area_table('上海'):=2;
    area_table('廣州'):=3;
    dbms_output.put_line('第一個元素:'||area_table.first);
    dbms_output.put_line('最后一個元素:'||area_table.last);
  end;

  8.2 嵌套表:元素個數從1開始,並且元素個數沒有限制
  定義嵌套表:
      type type_name is table of element_type;
      identifier type_name;
   
      declare
        type ename_table_type is table of emp.ename%type;
        ename_table ename_table_type:=ename_table_type('A','A');


  在PL/SQL塊中使用嵌套表:使用嵌套表變量時,必須首先使用構造方法初始化嵌套表變量,然后才能在塊內引用嵌套表元素
  declare
    type ename_talbe_type is table of emp.ename%type;
    ename_talbe ename_table_type;
  begin
    ename_table:=ename_table_type('mary','mary','mary');
    select ename into ename_table(2) from emp where empno=&no;
    dbms_output.put_line('雇員名:'||ename_table(2));
  end;


  在表列中使用嵌套表:
  在表列中使用嵌套表類型,必須首先使用CREATE TYPE命令建立嵌套表類型.
  當使用嵌套表類型作為表列的數據類型時,必須要為嵌套表列指定專門的存儲表
  create type phone_type is table of varchar2(20);
  /
  create table employee(
     id number(4),name varchar2(10),sal number(6,2),
     phone phone_type
  )nested table phone store as phone_table;

  8.3 在PL/SQL塊中為嵌套表列插入數據
  當定義嵌套表類型時,ORACLE自動為該類型生成相應的構造方法.當為嵌套表列插入數據時,需要使用嵌套表的構造方法
  begin
    insert into employee values(1,'scott',800,phone_type('0471-3456788','13804711111'));
  end;

  在PL/SQL塊中檢索嵌套表列的數據
  當在PL/SQL塊中檢索嵌套表列的數據時,需要定義嵌套表類型的變量接受其數據.
  set serveroutput on
  declare
    phone_table phone_type;
  begin
    select phone into phone_table from employee where id=1;
    for i in 1..phone_table.count loop
    dbms_output.put_line('電話:'||phone_table(i));
    end loop;
  end;

 
  8.4 在pl/sql塊中更新嵌套表列的數據
  更新嵌套表列的數據時,首先需要定義嵌套表變量,並使用構造方法初始化變量,然后才可在執行部分使用UPDATE語句更新數據
  declare
    phone_table phone_type:=phone_type('0471-3456788','13804711111','0471-2233066','13056278568');
  begin
    update employee set phone=phone_talbe where id=1;
  end;


  8.5變長數組(varray)
  VARRAY也是一種用於處理PL/SQL數組的數據類型, 它也可以做為表列的數據類型使用.
  元素下標以1開始,並且元素的最大個數是有限制的
  定義VARRAY的語法:
      type type_name is varray(size_limite) of element_type [not mull];
      identifier type_name;
  當使用VARRAY元素時,必須要使用其構造方法初始化VARRAY元素.

  declare 
    type ename_table_type is varrar(20) of emp.ename%type;
    ename_talbe ename_table_type:=ename_table_type('A','A');

  8.6 在PL/SQL塊中使用VARRAY
  必須首先使用其構造方法來初始化VARRAY變量,然后才能在PL/SQL塊內引用VARRAY元素
  declare
    type ename_table_type is varray(20) of emp.ename%type;
    ename_table ename_table_type:=ename_table_type('mary');
  begin
    select ename into ename_table(1) from emp where empno=&no;
    dbms_output.put_line('雇員名:'||ename_table(1));
  end;

  在表列中使用varray
  要在表列中引用該數據類型,則必須使用CREATE TYPE命令建立VARRAY類型
  create type phone_type is varray(20) of varchar2(20);
  /
  create table employee(
    id number(4),name varchar2(10),
    sal number(6,2),phone phone_type);
  在PL/SQL塊中操縱VARRAY列的方法與操縱嵌套表列的方法完全相同.嵌套表列元素個數沒有限制,而VARRAY列的元素個數是有限制的.
 

  PL/SQL記錄表
  PL/SQL變量處理單行單列數據
  PL/SQL記錄處理單行多列數據
  PL/SQL集合處理多行單列數據
  PL/SQL記錄表處理多行多列數據
  
  8.7 PL/SQL記錄表結合了PL/SQL記錄和PL/SQL集合的優點
  declare
    type emp_talbe_type is table of emp%rowtype index by binary_integer;
    emp_table emp_table_type;
  begin
    select * into emp_table(1) from emp where empno=&no;
    dbms_output.put_line('雇員姓名:'||emp_table(1).ename);
    dbms_output.put_line('雇員姓名:'||emp_table(1).sal);
  end;

  8.8 多級集合
  多級集合是指嵌套了集合類型的集合類型

  在PL/SQL塊中使用多級VARRAY:實現多維數組功能
  定義二維VARRAY(10,10):
  declare
    type a1_varray_type is varray(10) of int;--定義一維VARRAY
    type na1_varray_type is varray(10) of a1_varray_type;--定義二維VARRAY集合
    --初始化二維集合變量
    nv1 nal_varray_type:=nal_varray_type(
        a1_varray_type(58,100,102),
        a1_varray_type(55,6,73),
        a1_varray_type(2,4);
    begin
      dbms_output.put_line('顯示二維數組所有元素');
      for i in 1..nv1.count loop
          for j in 1..nv1(i).count loop
              dbms_output.put_line('nvl('||i||','||j||')='||nvl(i)(j));
          end loop;
      end loop;
   end;

  在PL/SQL塊中使用多級嵌套表
  如果多維數組的元素個數沒有限制,那么可以在嵌套表類型中嵌套另一個嵌套表類型
  
  8.9 二維嵌套表
  declare
    type a1_table_type is table of int;--定義一維嵌套表
    type nal_table_type is table of a1_table_type;--定義二維嵌套表集合
    --初始化二維集合變量
    nvl nal_table_type:=nal_table_type(
        a1_table_type(2,4),
        a1_table_type(5,73));
  begin
    dbms_output.put_line('顯示二維數組所有元素');
    for i in 1..nvl.count loop
       for j in 1..nvl(i).count loop
           dbms_output.put_line('nvl('||i||','||j||')='||nvl(i)(j));
       end loop;
    end loop;
  end

  在PL/SQL塊中使用多級索引表
  二維索引表:
  declare 
    type a1_table_type is table of int index by binary_integer;
    type nal_table_type is table of al_table_type index by binary_integer;
    nvl nal_table_type;
  begin
    nvl(1)(1):=10;
    nvl(1)(2):=5;
    nvl(2)(1):=100;
    nvl(2)(2):=50;
    dbms_output.put_line('顯示二維數組所有元素');
    for i in 1..nvl.count loop
       for j in 1..nvl(i).count loop
          dbms_output.put_line('nvl('||i||','||j||')='||nvl(i)(j));
       end loop;
    end loop;
  end;

  
  8.10集合方法:ORACLE提供的用於操縱集合變量的內置函數或過程,其中EXISTS,COUNT,LIMIT,FIRST,NEXT,FRIOR和NEXT是函數
           而EXTEND,TRIM和DELETE則是過程
  集合方法只能在PL/SQL語句中使用,不能在SQL語句中使用.
  集合方法EXTEND和TRIM只適用於嵌套表和VARRAY,而不適合於索引表

  1.EXISTS:用於確定集合元素是否存在
  declare 
    type ename_table_type is table of emp.ename%type;
    ename_table ename_table_type;
  begin
    if ename_table.exists(1) then
       ename_table(1):='SCOTT';
    else
       dbms_output.put_line('必須初始化集合元素');
    end if;
  end;

  2.COUNT:用於返回當前集合變量中的元素總個數.
  declare
    type ename_table_type is table of emp.ename%type index by binary_integer;
    ename_table ename_table_type;
  begin
    ename_table(-5):='scott';
    ename_table(1):='smith';
    ename_table(5):='mary';
    ename_table(10):='blake';
    dbms_output.put_line('集合元素總個數:'||ename_table.count);
  end;

  3.LIMIT:用於返回集合元素的最大個數.因為嵌套表和索引表的余數個數沒有限制,返回NULL
        對於VARRAY來說,該方法返回VARRAY所允許的最大元素個數
  declare
    type ename_table_type is varray(20) of emp.ename%type;
    ename_table ename_table_type:=ename_table_type('mary');
  begin
    dbms_output.put_line('集合元素的最大個數:'||ename_table.limit);
  end;

  4.FIRST和LAST:FIRST用於返回集合變量第一個元素的下標,而LAST方法則用於返回集合變量最后一個元素的下標
  declare
    type ename_table_type is table of emp.ename%type index by binary_integer;
    ename_table ename_table_type;
  begin
    ename_table(-5):='scott';
    ename_table(1):='smith';
    ename_table(5):='mary';
    ename_table(10):='blake';
    dbms_output.put_line('第一個元素:'||ename_table.first);
    dbms_output.put_line('最后一個元素:'||ename_table.last);
  end;

  5.FRIOR和NEXT:PRIOR返回當前集合元素的前一個元素的下標,而NEXT方法則用於返回當前集合元素的后一個元素的下標
  declare
    type ename_table_type is table of emp.ename%type index by binary_integer;
    ename_table ename_table_type;
  begin
    ename_table(-5):='scott';
    ename_table(1):='smith';
    ename_table(5):='mary';
    ename_table(10):='blake';
    dbms_output.put_line('元素5的前一個元素:'||ename_table.prior(5));
    dbms_output.put_line('元素5的后一個元素:'||ename_table.next(5));
  end;

  6.EXTEND:用於擴展集合變量的尺寸,並為它們增加元素.只適用於嵌套表和VARRAY.
         三種調用格式:EXTEND,EXTEND(N),EXTEND(N,I):添加N個元素,值與第I個元素相同
  declare
    type ename_table_type is varray(20) of varchar2(10);
    ename_table ename_table_type;
  begin
    ename_table:=ename_table_type('mary');
    ename_table.extend(5,1);
    dbms_output.put_line('元素個數:'||ename_table.count);
  end;

  7.TRIM:用於從集合尾部刪除元素,有TRIM和TRIM(N)兩種調用格式.
       只適用於嵌套表和VARRAY
  declare
    type ename_table_type is table of varchar2(10);
    ename_table ename_table_type;
  begin
    ename_table:=ename_table_type('a','a','a','a','a');
    ename_table.trim(2);
    dbms_table.put_line('元素個數:'||ename_table.count);
  end;

  8.DELETE:刪除結合元素,但該方法只適用於嵌套表和索引表,不適用於VARRAY.
         有DELETE,DELETE(N),DELETE(M,N)三種調用方式.
         DETELE(M,N)刪除集合變量從M到N之間的所有元素
  declare
    type ename_table_type is table of emp.ename%type index by binary_integer;
    ename_table ename_table_type;
  begin
    ename_table(-5):='scott';
    ename_table(1):='smith';
    ename_table(5):='mary';
    ename_table(10):='blake';
    ename_table.delete(5);
    dbms_output.put_line('元素總個數:'||ename_table.count);
  end;

  
  8.11集合賦值
  使用嵌套表和VARRAY時,通過執行INSERT,UPDATE,FETCH,SELECT賦值語句,可以將一個集合的數據賦值給另一個集合.
  當給嵌套表賦值時,還可以使用SET,MULTISET UNION,MULTISET INTERSECT,MULTISET EXCEPT等集合操作符
  SET:用於取消嵌套表中的重復值.
  MULTISET UNION:取得兩個嵌套表的並集(DISTINCT)
  MULTISET INTERSECT:用於取得兩個嵌套表的交集.
  NULTISET EXCEPT:用於取得兩個嵌套表的差集
  
  1.將一個集合的數據賦值個另一個集合
    源集合和目標集合的數據類型必須完全一致.
  declare
    type name_varray_type is varray(4) of varchar2(10);
    name_array1 name_varray_type;
    name_array2 name_varray_type;
  begin
    name_array1:=name_varray_type('scott','smith');
    name_array2:=name_varray_type('a','a','a','a');
    dbms_output.put_line('name_array2的原數據:');
    for i in 1..name_array2.count loop
       dbms_output.put_line(' '||name_array2(i));
    end loop;
    dbms_output.new_line;
    name_array2:=name_array1;
    dbms_output.put('name_array2的新數據:');
    for i in 1..name_array2.count loop
       dbms_output.put(' '||name_array2(i));
    end loop;
    dbms_output.new_line;
  end;


  2.給集合賦NULL值:清空集合變量的所有數據(集合方法DETELE,TRIM也可以)
  declare
    type name_varray_type is varray(4) of varchar2(10);
    name_array name_varray_type;
    name_empty name_varray_type;
  begin
    name_array:=name_varray_type('scott','smith');
    dbms_output.put_line('name_array的原有元素個數:'||name_array.count);
    name_array:=name_empty;
    if name_array is null then      
       dbms_output.put_line('name_array的現有元素個數:0');
    end if;
  end;

  3.使用集合操作符給嵌套表賦值
    
    1.使用SET操作符:用於取消特定嵌套表中的重復值.
  declare
    type nt_table_type is table of number;
    nt_table nt_table_type:=nt_table_type(2,4,3,1,2);
    result nt_table_type;
  begin
    result:=set(nt_table);
    dbms_output.put('result:');
    for i in 1..result.count loop
      dbms_output.put(' '||result(i));
    end loop;
    dbms_output.new_line;
  end;

    2.使用MULTISET UNION操作符:取得兩個嵌套表的並集.結果集中會包含重復值
  declare
    type nt_table_type is table of number;
    nt1 nt_table_type:=nt_table_type(1,2,3);
    nt2 nt_table_type:=nt_table_type(3,4,5);
    result nt_table_type;
  begin
    result:=nt1 MULTISET union nt2;
    dbms_output.put('result:');
    for i in 1..result.count loop
      dbms_output.put(' '||result(i));
    end loop;
    dbms_output.new_line;
  end;

  3.使用MULTISET UNION DISTINCT操作符:用於取得兩個嵌套表的並集,並取消重復結果.
  declare
    type nt_table_type is table of number;
    nt1 nt_table_type:=nt_table_type(1,2,3);
    nt2 nt_table_type:=nt_table_type(3,4,5);
    result nt_table_type;
  begin
    result:=nt1 multiset union distinct nt2;
    dbms_output.put('result:');
    for i in 1..result.count loop
      dbms_output.put(' '||result(i));
    end loop;
    dbms_output.new_line;
  end;
  
  4.使用MULTISET INTERSECT操作符:用於取得兩個嵌套表的交集
  declare
    type nt_table_type is table of number;
    nt1 nt_table_type:=nt_table_type(1,2,3);
    nt2 nt_table_type:=nt_table_type(3,4,5);
    result nt_table_type;
  begin
    result:=nt1 multiset intersect nt2;
    dbms_output.put('result:');
    for i in 1..result.count loop
      dbms_output.put(' '||result(i));
    end loop;
    dbms_output.new_line;
  end;
  
  5.使用MULTISET EXCEPT操作符:取得兩個嵌套表的差集.在NT1中存在,但在NT2中不存在
  declare
    type nt_table_type is table of number;
    nt1 nt_table_type:=nt_table_type(1,2,3);
    nt2 nt_table_type:=nt_table_type(3,4,5);
    result nt_table_type;
  begin
    result:=nt1 multiset except nt2;
    dbms_output.put('result:');
    for i in 1..result.count loop
       dbms_output.put(' '||result(i));
    end loop;
    dbms_output.new_line;
  end;

  8.4比較集合
  函數cardinality用於返回嵌套表變量的元素個數   
  操作符SUBMULTISET OF用於確定一個嵌套表是否為另一個嵌套表的子集
  操作符MEMBER OF用於檢測特定數據是否為嵌套表元素
  操作符IS A SET用於檢測嵌套表是否包含重復的元素值
  操作符IS EMPTY用於檢測嵌套表是否為NULL.
  
  1.檢測集合是否為NULL
  declare
    type name_table_type is table of varchar2(10);
    name_table name_table_type;
  begin
    if name_table is empty then
       dbms_output.put_line('name_table未初始化');
    end if;
  end;

  2.比較嵌套表是否相同
  使用比較符=和!=檢測兩個嵌套表是否相同.不能比較VARRAY和索引表
  declare
    type name_table_type is table of varchar2(10);
    name_table1 name_table_type;
    name_table2 name_table_type;
  begin
    name_table1:=name_table_type('scott');
    name_table2:=name_table_type('smith');
    if name_table1=name_table2 then
      dbms_output.put_line('兩個嵌套表完全相同');
    else
      dbms_output.put_line('兩個嵌套表數值不同');
    end if;
  end;

  3.在嵌套表上使用集合操作符
    在嵌套表上使用ANSI集合操作符CARDINALITY,MEMBER OF, IS A SET.不適用於VARRAY和索引表
    使用函數CARDINALITY
  declare
    type nt_table_type is table of number;
    nt1 nt_table_type:=nt_table_type(1,2,3,1);
  begin
    dbms_output.put_line('元素個數:'||cardinality(nt1));
  end;

    使用操作符SUBMULTISET OF:用於確定一個嵌套表是否為另一個嵌套表的子集.
  declare
    type nt_table_type is table of number;
    nt1 nt_table_type:=nt_table_type(1,2,3);
    nt2 nt_table_type:=nt_table_type(1,2,3,4);
  begin
    if nt1 submultiset of nt2 then
       dbms_output.put_line('nt1是nt2的子集);
    end if;
  end;

  使用操作符MEMBER OF :用於檢測特定數據是否為嵌套表的元素.
  declare
    type nt_table_type is table of number;
    nt1 nt_table_type:=nt_table_type(1,2,3,5);
    v1 number:=&v1;
  begin
    if v1 MEMBER OF nt1 then
      dbms_output.put_line('v1是nt1的元素');
    end if;
  end;

  使用操作符IS A SET:用於檢測嵌套表是否包含重復的元素值
  declare
    type nt_table_type is table of number;
    nt1 nt_table_type:=nt_table_type(1,2,3,5);
  begin
    if nt1 is a set then
      dbms_output.put_line('嵌套表NT1無重復值');
    end if;
  end;

 


  8.5批量綁定
  當在select,insert,update,delete語句上處理批量數據時,通過批量綁定,可以極大的加快數據處理速度,提高應用程序性能
  批量綁定是使用BULK COLLECT子句和FORALL語句來完成的
  BULK COLLECT 子句用於取得批量數據,該子句只能用於SELECT語句,FETCH語句和DML返回子句
  FORALL只適用於執行批量的DML操作

  1.不使用批量綁定
  declare
    type id_table_type is table of number(6) index by binary_integer;
    type name_table_type is table of varchar2(10) index by binary_integer;
    id_table id_table_type;
    name_table name_table_type;
    start_time number(10);
    end_time number(10);
  begin
    for i in 1..5000 loop
       id_table(i):=i;
       name_table(i);='name'||to_char(i);
    end loop;
    start_time:=dbms_utility.get_time;
    for i in 1..id_table.count loop
        insert into demo values(id_table(i),name_table(i));
    end loop;
    end_time:=dbms_utility.get_time;
    dbms_output.put_line('總計時間(秒):'||to_char((end_time-start_time)/100));
  end;

  2.使用批量綁定
   declare
    type id_table_type is table of number(6) index by binary_integer;
    type name_table_type is table of varchar2(10) index by binary_integer;
    id_table id_table_type;
    name_table name_table_type;
    start_time number(10);
    end_time number(10);
  begin
    for i in 1..5000 loop
       id_table(i):=i;
       name_table(i);='name'||to_char(i);
    end loop;
    start_time:=dbms_utility.get_time;
    forall i in 1..id_table.count
       insert into demo values(id_table(i),name_table(i));
    end_time:=dbms_utility.get_time;
    dbms_output.put_line('總計時間(秒):'||to_char((end_time-start_time)/100));
  end;


  8.6 FORALL語句
  執行批量insert,update,delete操作時,使用forall語句,FORALL不是循環語句
  oracle9i當使用FORALL語句時,必須具有連續的元素
  oracle10g通過使用indices of和values of子句,可以使用不連續的集合元素.
  forall三種執行語法:
  forall index in lower_bound..upper_bound sql_statement;
  forall index in indices of collection [between lower_bond.and.upper_bound] sql_statement;
  forall index in values of index_collection sql_statement;

  1.在insert語句上使用批量綁定
  declare
    type id_table_type is table of number(6) index by binary_integer;
    type name_table_type is table of varchar2(10) index by binary_integer;
    id_table id_table_type;
    name_table name_table_type;
  begin
    for i in 1..10 loop
      id_table(i):=i;
      name_table(i):='name'||to_char(i);
    end loop;
    forall i in 1..id_table.count
        insert into demo values(id_table(i),name_table(i));
  end;

  2.在update語句上使用批量綁定
   declare
    type id_table_type is table of number(6) index by binary_integer;
    type name_table_type is table of varchar2(10) index by binary_integer;
    id_table id_table_type;
    name_table name_table_type;
  begin
    for i in 1..5 loop
      id_table(i):=i;
      name_table(i):='n'||to_char(i);
    end loop;
    forall i in 1..id_table.count 
       update demo set name=name_table(i) where id=id_table(i);
  end;

  3.在DELETE語句上使用批量綁定
  declare
    type id_table_type is table of number(6) index by binary_integer;
    id_table id_table_type;
  begin
    for i in 1..3 loop
      id_table(i):=i;
    end loop;
    forall i in 1..id_table.count 
      delete from demo where id=id_table(i);
  end;

  4.在FORALL語句中使用部分集合元素
  declare
    type id_table_type is table of number(6) index by binary_integer;
    id_table id_table_type;
  begin
    for i in 1..10 loop
      id_table(i):=11-i;
    end loop;
    forall i in 8..10
       insert into demo (id) values (id_table(i));
  end;

  5.在FORALL語句上使用INDECES OF子句:用於跳過NULL集合元素
  declare
    type id_table_type is table of number(6);
    id_table id_table_type;
  begin
    id_table:=id_table_type(1,null,3,null,5);
    forall i in indices of id_table
       delete from demo where id=id_table(i);
  end;
 
  6.在FORALL語句上使用VALUES OF子句
  create table new_demo as select * from demo where 1=0
  declare
    type id_table_type is table of demp.id%type;
    type name_table_type is table of demo.name%type;
    id_table id_table_type;
    name_table name_table_type;
    type index_pointer_type is table of pls_integer;
    index_pointer index_pointer_type;
  begin
    select * bulk collect into id_table,name_table from demo;
    index_pointer:=index_pointer_type(6,8,10);
    forall i in values of index_pointer
        insert into new_demo values(id_table(i),name_table(i));
  end;

  7.使用SQL%BULK_ROWCOUNT屬性:專門為FORALL語句提供,用於取得在執行批量綁定操作時第i個元素所作用的行數
  declare
    type dno_table_type is table of number(3);
    dno_table dno_table_type:=dno_table_type(10,20);
  begin
    forall i in 1..dno_table.count
       update emp set sal=sal*1.1 where deptno=dno_table(i);
       dbms_output.put_line('第2個元素更新的行數:'||sql%bulk_rowcount(2));
  end;

 
  8.7 BULK COLLECT子句
  用於取得批量數據,只適用於select into 語句,fetch into 語句和DML返回子句
  可將批量數據存放到PL/SQL集合變量中
  1.在select into 語句中使用BULK COLLECT 子句:可以一次將SELECT語句的多行結果檢索到集合變量中
  declare
    type emp_table_type is table of emp%rowtype index by binary_integer;
    emp_table emp_table_type;
  begin
    select * bulk collect into emp_table from emp where deptno=&no;
    for i in 1..emp_table.count loop
       dbms_output.put_line('雇員姓名:'||emp_table(i).ename);
    end loop;
  end;

  2.在DML的返回語句中使用BULK COLLECT子句
  為了取得DML操作所改變的數據,可以使用RETURNING子句.
  declare
    type ename_table_type is table of emp.ename%type;
    ename_table ename_table_type;
  begin
    delete from emp where deptno=&no;
      returning ename bulk collect into ename_table;
    dbms_output.put('雇員名');
    for i in 1..ename_table.count loop
       dbms_output.put(ename_table(i)|| ' ');
    end loop;
    dbms_output.new_line;
  end;


 

9.使用游標
  9.1當在PL/SQL塊中執行查詢語句SELECT和數據操縱語句DML時,ORACLE會為其分配上下文區(CONTEXT AREA),游標指上下文區指針
  對於數據操縱語句和單行SELECT INTO語句來說,ORACLE會為他們分配隱含游標.
  使用顯示游標處理多行數據,也可使用SELECT..BULK COLLECT INTO 語句處理多行數據.

  1.顯示游標
    定義游標
    cursor cursor_name is select_statement;
  2.打開游標:執行對應的SELECT語句並將SELECT語句的結果暫時存放到結果集中.
    open cursor_name;
  3.提取數據
    打開游標后,SELECT語句的結果被臨時存放到游標結果集中,使用FETCH語句只能提取一行數據
    通過使用FETCH..BULK COLLECT INTO語句每次可以提取多行數據
    fetch cursor_name into variable1,varibale2,...;
    fetch cursor_name bulk collect into collect1,collect2,...[limit rows];
  4.關閉游標
    close cursor_name;
  
  9.2顯示游標屬性
    用於返回顯示游標的執行信息,包括%isopen,%found,%notfound,%rowcount
  1.%isopen:確定游標是否打開 if cl%isopen then ... else  open c1; end if;
  2.%found:檢查是否從結果集中提取到了數據 
    loop
      fetch c1 into var1,var2;
      if c2%found then ... else exit;
    end loop;
  3.%notfound
    loop 
       fetch c1 into var1,var2;
       exit when c2%notfound;
       ...
    end loop;
  4.%rowcount:返回當前行為止已經提取到的實際行數
    loop 
      fetch c1 into my_ename,my_deptno;
      if c1%rowcount>10 then
      ...
      end if;
      ...
    end loop;
  
  9.3顯示游標使用示例
  1.在顯示游標中使用fetch..into語句:只能處理一行數據,除非用循環語句
  declare
    cursor emp_cursor is select ename,sal from emp where deptno=10;
    v_ename emp.ename%type;
    v_sal emp.sal%type;
  begin
    open emp_cursor;
    loop
      fetch emp_cursor into v_ename,v_sal;
      exit when emp_cursor%notfound;
      dbms_output.put_line(v_ename||': '||v_sal);
   end loop;
   close emp_cursor;
  end;

  2.在顯示游標中,使用FETCH..BALK COLLECT INTO語句提取所有數據
  declare
    cursor emp_cursor is select ename from emp where deptno=10;
    type ename_table_type is table of varchar2(10);
    ename_table ename_table_type;
  begin
    open emp_cursor;
    fetch emp_cursor bulk collect into ename_table;
    for i in 1..ename_table.count loop
        dbms_output.put_line(ename_table(i));
    end loop;
    close emp_cursor;
  end;

  3.在顯示游標中使用FETCH..BULK COLLECT INTO ..LIMIT語句提取部分數據
  declare
    type name_array_type is varray(5) of varchar2(10);
    name_array name_array_type;
    cursor emp_cursor is select ename from emp;
    rows int:=5;
    v_count int:=0;
  begin
    open emp_cursor;
    loop
      fetch emp_cursor bulk collect into name_array limit rows;
      dbms_output.pur('雇員名');
      for i in 1..(emp_currsor%rowcount-v_count) loop 
        dbms_output.put(name_array(i)||' ');
      end loop;
      dbms_output.new_line;
     v_count:=emp_cursor%rowcount;
     exit when emp_cursor%notfound;
    end loop;
    close emp_cursor;
  end;

  4.使用游標屬性
  declare
    cursor emp_cursor is select ename from emp where deptno=10;
    type ename_table_type is table of varchar2(10);    
    ename_table ename_table_type;
  begin
    if not emp_cursor%isopen then 
       open emp_cursor;
    end if;
    fetch emp_cursor bulk collect into ename_table;
    dbms_output.put_line('提取的總計行數:'||emp_cursor%rowcount);
    close emp_cursor;
  end;

  5.基於游標定義記錄變量
  declare
    cursor emp_cursor is select ename,sal from emp;
    emp_record emp_cursor%rowtype;
  begin
    open emp_cursor;
    loop
      fetch emp_cursor into emp_record;
      exit when emp_cursor%notfound;
      dbms_output.put_line('雇員名:'||emp_record.ename||',雇員工資:'||emp_record.sal);
    end loop;
  end;

 

 9.4參數游標
  定義參數游標時,游標參數只能指定數據類型,而不能指定長度.
  cursor cursor_name(parameter_name datatype) is select_statment;
  declare
    cursor emp_cursor(no number) is select ename from emp where deptno=no;
    v_ename emp.ename%type;
  begin
    open emp_cursor(10);
    loop
      fetch emp_cursor into v_ename;
      exit when emp_cursor%notfound;
      dbms_output.put_line(v_ename);
    end loop;
    close emp_cursor;
  end;

  9.5使用游標更新或刪除數據
  要通過游標更新或刪除數據,在定義游標時必須要帶有FOR UPDATE子句
  cursor cursor_name(parameter_name datetype) is select_statement for update [of column_reference] [nowait];
  for update子句用於在游標結果集數據上家行共享鎖,防止其他用戶在相應行執行DML操作
  of子句確定哪些表要加鎖,沒有OF子句,則在所引用的全部表上加鎖
  nowait子句用於指定不等待鎖
  必須在UPDATE后DELETE語句中引用WHERE CURRENT OF子句
  update table_name set column=.. where current of cursor_name;
  delete table_name where current of cursor_name;
 

  1.使用游標更新數據
  declare
    cursor emp_cursor is select ename,sal from emp for update;
    v_ename emp.ename%type;
    v_sal emp.sal%tyep;
  begin
    open emp_cursor;
    loop
      fetch emp_cursor into v_ename,v_oldsal;
      exit when emp_cursor%notfound;
      if v_oldsal<2000 then
         update emp set sal=sal+100 where current of emp_cursor;
      end if;
    end loop;
    close emp_cursor;
  end;

  2.使用游標刪除數據
  declare
    cursor emp_cursor is select ename,sal,deptno from emp for update;
    v_ename emp.ename%type;
    v_oldsal emp.sal%type;
    v_deptno emp.deptno%type;
  begin
    open emp_cursor;
    loop
      fetch emp_cursor into v_ename,v_oldsal,v_deptno;
      exit when emp_cursor%notfound;
      if v_deptno=30 then
         delete from emp where current of emp_cursor;
      end if;
    end loop;
    close emp_cursor;
  end;

  3.使用OF子句在特定表上加行共享鎖
  declare
    cursor emp_cursor is select ename,sal,dname,emp.deptno from emp,dept where emp.deptno=dept.deptno
    for update of emp.deptno;
    emp_record emp_cursor%type;
  begin
    open emp_cursor;
    loop
      fetch emp_cursor into emp_record;
      exit when emp_cursor%notfound;
      if emp_record.deptno=30 then
         update emp set sal=sal+100 where current of emp_cursor;
      end if;
      dbms_output.put_line('雇員名:'||emp_record.ename||',工資:'||emp_record.sal||',部門名:'||emp_record.dname);
    end loop;
    close emp_cursor;
  end;

  4.使用nowait子句
  通過在FOR UPDATE子句中指定NOWAIT語句,可以避免等待鎖.若已經被作用行加鎖,則提示錯誤信息
  declare
    cursor emp_cursor is select ename,sal from emp for update nowait;
    v_ename emp.ename%type;
    v_oldsal emp.sal%type;
  begin
    open emp_cursor;
    loop
      fetch emp_cursor into v_ename,v_sal;
      exit when emp_cursor%notfound;
      if v_oldsal<2000 then
        update emp set sal=sal+100 where current of emp_cursor;
      end if;
    end loop;
    close emp_cursor;
  end;

 

  9.6游標FOR循環
  使用FOR循環時,ORACLE會隱含的打開游標,提取游標數據並關閉游標
  for record_name in cursor_name loop
      statement1;
      statement2;
      ...
  end loop;
  每循環一次提取一次數據,在提取了所有數據后,自動退出循環並隱含的關閉游標
  1.使用游標FOR循環  
  declare
    cursor emp_cursor is select ename,sal from emp;
  begin
    for emp_record in emp_cursor loop
      dbms_output.put_line('第'||emp_curosr%rowcount||'個雇員: '||emp_record.ename);
    end loop;
  end;

  2.在游標FOR循環中直接使用子查詢
  begin
    for emp_record in (select ename,sal from emp) loop
      dbms_output.put_line(emp_record.ename);
    end loop;
  end;

  9.7使用游標變量
  PL/SQL的游標變量中存放着指向內存地址的指針.
  
  1.游標變量使用步驟
    包括定義游標變量,打開游標,提取游標數據,關閉游標等四個階段
  1.1定義ref cursor類型和游標變量
  type ref_type_name is ref cursor [return return_type];
  cursor_varibale ref_type_name;
  當指定RETURN子句時,其數據類型必須是記錄類型,不能在包內定義游標變量
  1.2打開游標
  open cursor_variable for select_statement;
  1.3提取游標數據
  fetch cursor_varibale into variable1,variable2,...;
  fetch cursor_varibale bulk collect into collect1,collect2,...[limit rows]
  1.4關閉游標變量
  close cursor_varibale;

  2.游標變量使用示例
  2.1在定義FEF CURSOR類型時不指定RETURN子句
  在打開游標時可以指定任何的SELECT語句
  declare
    type emp_cursor_type is ref cursor;
    emp_cursor emp_cursor_type;
    emp_record emp%rowtype;
  begin
    open emp_cursor for select * from emp where deptno=10;
    loop
      fetch emp_cursor into emp_record;
      exit when emp_cursor%notfound;
      dbms_output.put_line('第'||emp_curosr%rowcount||'個雇員: '||emp_record.ename);
    end loop;
    close emp_cursor;
  end;

  2.2在定義REF CURSOR類型時指定RETURN子句
  在打開游標時SELECT語句的返回結果必須與RETURN子句所指定的記錄類型相匹配.
  declare
    type emp_record_type is record(name varchar2(10),salary number(6,2));
    type emp_cursor_type is ref cursor return emp_record_type;
    emp_cursor emp_cursor_type;
    emp_record emp_record_type;
  begin
    open emp_cursor for select ename,sal from emp where deptno=20;
    loop
      fetch emp_cursor into emp_record;
      exit when emp_cursor%notfound;
      dbms_output.put_line('第'||emp_curosr%rowcount||'個雇員: '||emp_record.ename);
    end loop;
    close emp_cursor;
  end;

 

  9.7使用CURSOR表達式
  CURSOR表達式用於返回嵌套游標
  結果集不僅可以包含普通數據,而且允許包含嵌套游標的數據
  cursor(subquery)

  declare
    type refcursor is ref cursor;
    cursor dept_cursor(no number) is select a.dname,cursor(select ename,sal from emp where deptno=a.deptno)
    from dept a where a.deptno=no;
    empcur refcursor;
    v_dname dept.dname%type;
    v_ename emp.ename%type;
    v_sal emp.sal%type;
  begin
    open dept_cursor(&no);
    loop
      fetch dept_cursor into v_danme,empcur;
      exit when dept_cursor%notfound;
      dbms_output.put_line('部門名:'||v_dname);
      loop
        fetch empcur into v_ename,v_sal;
        exit when empcur%notfound;
        dbms_output.put_line('雇員名:'||v_ename||',工資:'||v_sal);
      end loop;
    end loop;
    close dept_cursor;
  end;
 
 

    
  10開發子程序:過程和函數
  過程:執行特定操作
  函數:用於返回特定數據
   
  10.1過程
  語法:create [or replace] procedure procedure_name(argument1 [model] datatype1,arguement2 [mode2],...)
       is [as] 
       pl/sql block;

  1.建立過程:不帶任何參數
  create or replace procecdure out_time
  is
  begin
    dbms_output.put_line(systimestemp);
  end;
  2.調用過程
  set serveroutput on
  exec out_time

  set serveroutput on
  call out_time();

  3.建立過程:帶有IN參數
  create or replace procedure add_employee
  (eno number,name varchar2,sal number,job varchar2 default 'clerk',dno number)
  is 
  e_integrity exception;
  pragma exception_init(e_integrity,-2291);
  begin
    insert into imp(empno,ename,sal,job,deptno) valres(eno,name,sal,job,dno);
  exception
    when dup_val_on_index then 
      raise_application_error(-20000,'雇員號不能重復');
    when e_integrity then
      raise_application_error(-20001,'部門不存在');
  end;

  exec add_employee(1111,'clark',2000,'manager',10)

  4.建立過程:帶有OUT參數
  create or replace procedure query_employee
  (eno number,name out varchar2,salary out number)
  is
  begin
    select ename,sal into name,salary from emp where empno=eno;
  exception
    when no_date_found then
      raise_application_error(-20000,'該雇員不存在');
  end;
  當在應用程序中調用該過程時,必須要定義變量接受輸出參數的數據
  sql>var name varchar2(10)
      var salary number
      exec query_employee(7788,:name,:salary)
      print name salary
  
 
  5.建立過程:帶有IN OUT參數(輸入輸出參數)
  create or replace procedure compute
  (num1 in out number,num2 in out number)
  is
    v1 number;
    v2 number;
  begin
    v1:num1/num2;
    v2:mod(num1,num2);
    num1:=v1;
    num2:=v2;
  end;

  sql>var n1 number
      var n2 number
      exec :n1:=100
      exec :n2:=30
      exec ecmpute(:n1,:n2)
      print n1 n2

  6.為參數傳遞變量和數據 
    位置傳遞,名稱傳遞,組合傳遞三種
    1.位置傳遞:在調用子程序時按照參數定義的順序為參數指定相應的變量或數值
      exec add_dept(40,'sales','new york');
      exec add_dept(10);
    2.名稱傳遞:在調用子程序時指定參數名,並使用關聯符號=>為其提供相應的數值或變量
      exec add_dept(dname=>'sales',dno=>50);
      exec add_dept(dno=>30);
    3.組合傳遞:同時使用位置傳遞和名稱傳遞
      exec add_dept(50,loc=>'new york');
      exec add_dept(60,dname=>'sales',loc=>'new york');

  7.查看過程原代碼
    oracle會將過程名,源代碼以及其執行代碼存放到數據字典中.執行時直接按照其執行代碼執行
    可查詢數據字典(user_source)
    select text from user_source where name='add_dept';
  
    刪除過程
    drop procedure add_dept;

  

  10.2函數
  用於返回特定函數
  語法:create [or replace] function function_name
              (argument1 [mode1] datatype1,
               argument2 [mode2] datatype2,
               .....)
       return datatype       --函數頭部必須要帶有RETURN子句,至少要包含一條RETURN語句
       is|as  pl/sql block;
   
  1.建立函數:比帶任何參數
  create or replace function get_user
  return varchar2
  is
    v_user varchar2(100);
  begin
    select username into v_user from user_users;
    return v_user;
  end;

  2.使用變量接受函數返回值
    sql>var v1 varchar2(100)
        exec :v1:=get_user
        print v1
    在SQL語句中直接調用函數
    select get_user from dual;
    使用DBMS_OUTPUT調用函數
    set serveroutput on
    exec dbms_output.put_line('當前數據庫用戶:'||ger_user)
 
  3.建立函數:帶有IN參數
    create or replace function get_sal(name in varchar2)
    return number
    as
      v_sal emp.sal%type;
    begin
      select sal into v_sal from emp where upper(ename)=upper(name);
      return v_sal;
    exception
      when no_data_found then
        raise_application_error(-20000,'該雇員不存在');
    end;

  4.建立函數:帶有out參數
  create or replace function get_info(name varchar2,title out varchar2)
  return varchar2
  as
    deptname dept.dname%type;
  begin
    select a.job,b.dname into title,deptname from emp a,dept b and a.deptno=b.deptno 
    and upper(a.ename)=upper(name);
    return deptname
  exception
    when no_data_found then
      raise_application_error(-20000,'該雇員不存在');
  end;

  sql>var job varchar2(20)
      var dname varchar2(20)
      exec :dname:=get_info('scott',:job)
      print danme job

  5.建立函數:帶有IN OUT參數
  create or replace function result(num1 number,num2 in out number)
  return number
  as
    v_result number(6);
    v_remainder number;
  begin 
    v_result:=num1/num2;
    v_remainder:=mod(num1,num2);
    num2:=v_remainder;
    return v_result;
  exception
    when zero_divide then
      raise_application_error(-20000,'不能除0');
  end;

  sql>var result1 number
      var result2 number
      exec :result2:=30
      exec :result1:=result(100,:result2)
      print result result2

  6.函數調用限制
  SQL語句中只能調用存儲函數(服務器端),而不能調用客戶端的函數
  SQL只能調用帶有輸入參數,不能帶有輸出,輸入輸出函數
  SQL不能使用PL/SQL的特有數據類型(boolean,table,record等)
  SQL語句中調用的函數不能包含INSERT,UPDATE和DELETE語句

  7.查看函數院源代碼
  oracle會將函數名及其源代碼信息存放到數據字典中user_source 
  set pagesize 40
  select text from user_source where name='result';
  8.刪除函數
  drop function result;
 
 

  10.3管理子程序
  1.列出當前用戶的子程序
  數據字典視圖USER_OBJECTS用於顯示當前用戶所包含的所有對象.(表,視圖,索引,過程,函數,包)
  sql>col object_name format a20
      select object_name,created,status from user_objects where object_type in ('procedure','function')
      
  2.列出子程序源代碼
  select text from user_source where name='raise_salsry';

  3.列出子程序編譯錯誤
  使用SHOW ERRORS命令確定錯誤原因和位置
  show errors procedure raise_salary
 
  使用數據字典視圖USER_ERRORS確定錯誤原因和位置
  col text format a50
  select line||'/'||position as "line/col",text error from user_errors where name='raise_salary';

  4.列出對象依賴關系
  使用數據字典視圖USER_DEPENDENCIES確定直接依賴關系
  select name,type from user_dependencies where referenced_name='emp';

  使用工具視圖DEPTREE和IDEPTREE確定直接依賴和間接依賴關系
  先運行SQL腳本UTLDTREE.SQL來建立這兩個視圖和過程DEPTREE_FILL,然后調用DEPTREE_FILL填充這兩個視圖
  sql>@%oracle_home%\rdbms\admin\utldtree
      exec deptree_fill('TABLE','scott','emp')
  執行后會將直接或間接依賴於SCOTT.EMP表的所有對象填充到視圖DEPTREE和IDEPTREE中.
      select nested_level,name,type from deptree;
      select * from ideptree

  5.重新編譯子程序
  當修改了被引用對象的結構時,就會將相關依賴對象轉變為無效(INVALID)狀態。
  alter table emp add remark varchar2(10);
  select object_name,object_type from user_objects where status='invalid';
  為了避免子程序的運行錯誤,應該重新編譯這些存儲對象
  alter procedure add_employee compile;
  alter view dept10 compile;
  alter function get_info compile;

 


  11.開發包
  包用於邏輯組合相關的PL/SQL類型,項和子程序,由包規范和包體組成
  1.建立包規范:包規范是包與應用程序之間的接口,用於定義包的公用組件,包括常量,變量,游標,過程,函數等
  create [or replace] package package_name
  is|as
     public type and item declarations
     subprogram specificationsend package_name;

  create or replace package emp_package is
    g_deptno number(3):=30;
    procedure add_employee(eno number,name varchar2,salary number,dno number default g_deptno);
    procedure fire_employee(eno number);
    function get_sal(eno number) return number;
  end emp_package;

  2.建立包體:用於實現包規范所定義的過程和函數
  create [or replace] package body package_name
  is|as
     private type and item declarations
     subprogram bodies
  end package_name;

  create or repalce package body emp_package is

    function validate_deptno(v_deptno number)
      return boolean
    is 
      v_temp int;
    begin
      select 1 into v_temp from dept where deptno=v_deptno;
      return true;
    exception
      when no_date_found then
        return false;
    end;


    procedure add_employee(eno number,name varchar2,salary number,dno number default g_deptno)
    is
    begin
      if validate_deptno(dno) then
         insert into emp(empno,ename,sal,deptno) values(eno,name,salsry,dno);
      else
         raise_application_error(-20010,'不存在該部門');
      end if;
    exception
      when dup_val_on_index then
         raise_application_error(-20012,'該雇員已存在');
    end;


    procedure fire_employee(eno number) is
    begin
      delete from emp where empno=eno;
      if sql%notfound then 
         raise_application_error(-20012,'該雇員不存在');
      end if;
    end;
    
    function get_sal(eno number) return number
    is
      v_sal emp.sal%type;
    begin
      select sal into v_sal from emp where empno=eno;
      return v_sal;
    exception
      when no_data_found then
        raise_application_error(-20012,'該雇員不存在');
    end;
  end emp_package;

  3.調用包組件
  3.1在同一個包內調用包組件
  create or replace package body emp_package is
  
  procedure add_employee(eno number,name vauchar2,salary number,dno number default g_deptno)
  is 
  begin
    if validate_deptno(dno) then
       insert into emp(empno,ename,sal,deptno) values(eno,name,salary,dno);
    else
       raise_application_error(-20010,'該部門不存在')
    end if;
    exception
      when dup_val_on_index then
       raise_application_error(-20011,'該雇員已存在')
    end;
    .........

  3.2調用包公用變量
  exec emp_package.g_deptno:=20
  3.3調用包公用過程
  exec emp_package.add_employee(1111,'mary',2000)
  3.4調用包公用函數 
  var salary number
  exec :salary:=emp_package.get_sal(7788)
  print salary
  3.5以其他用戶身份調用包公用組件
  conn system/manager
  exec scott.emp_package.add_employee(1115,'scott',1200)
  exec scott.emp_package.fire_employee(1115)
  3.6調用遠程數據庫包的公用組件
  exec emp_package.add_employee@orasrv(1116,'scott',1200)
 
  4.查看源代碼:存放在數據字典USER_SCOURCE中
  select text from user_source where name='emp-package' and type='package';
  5.刪除包
  drop package emp_package;
  
  
  6.使用包重載
  重載(overload)是指多個具有相同名稱的子程序
  1.建立包規范   
  同名的過程和函數必須具有不同的輸入參數,同名函數返回值的數據類型必須完全相同
  create or replace package overload is
    function get_sal(eno number) return number;
    function get_sal(name varchar2) return number;
    procedure file_employee(eno number);
    procedure file_employee(name varchar2);
  end;

  2.建立包體
  必須要給不同的重載過程和重載函數提供不同的實現代碼
  create or replace package body overload is
   
    function get_sal(eno number) return number
    is 
      v_sal emp.sal%type;
    begin
      select sal into v_sal from emp where empno=eno;
      return v_sal;
    exception
      when no_data_found then
        raise_application_error(-20020,'該雇員不存在');
    end;

    function get_sal(name varchar2) return  number
    is
      v_sal emp.sal%type;
    begin
      select sal into v_sal from emp where upper(ename)=upper(name);
      return v_sal;
    exception
      when no_data_found then
        raise_application_error(-20020,'該雇員不存在');
    end;

    procedure fire_employee(eno number) is
    begin
      delete from emp where empno=no;
      if sql%notfound then
        raise_application_error(-20020,'該雇員不存在');
      end if;
    end;

    procedure fire_employee(name varchar2) is
    begin
      delete from emp where upper(ename)=upper(name);
      if sql%notfound then
        raise_application_error(-20020,'該雇員不存在');
      end if;
    end;
  end;

  3.調用重載過程和重載函數
  var sal1 number
  var sal2 number
  exec :sal1:=overload.get_sal('scott')
  exec :sal2:=overload.get_sal(7685)
  exec overload.fire_employee(7369)
  exec overload.fire_employee('scott')

  7.使用包構造過程
  類似於高級語言中的構造函數和構造方法
  1.建立包規范
  包的構造過程用於初始化包的全局變量.
  create or replace package emp_package is
    minsal number(6,2);
    maxsal number(6,2);
    procedure add_employee(eno number,name varchar2,salary number,dno number);
    procedure upd_sal(eno number,salary number);
    procedure upd_sal(name varchar2,salary number);
  end;
 
  2.建立包體
  包的構造過程沒有任何名稱,它是實現了包的其他過程后,以BEGIN開始,END結束的部分
  create or replace package body emp_package is
    
    procedure add_employee(eno number,name varchar2,salary number,dno number)
    is
    begin
    if salary between minsal and maxsal then
      insert into emp (empno,ename,sal,deptno) values(eno,name,salary,dno);
    else
      raise_application_error(-20001,'工資不在范圍內');
    end if;
    exception
      when dup_val_on_index then
        raise_application_error(-20002,'該雇員已經存在');
    end;
 
    procedure upd_sal(eno number,salary number) is
    begin
      if salary between minsal and maxsal then
         update emp set sal=salary where empno =eno;
         if sql%notfound then
           raise_application_error(-20003,'不存在雇員號');
         end if;
      else
         raise_application_errpr(-20001,'工資不在范圍內');
      end if;
   end;
   procedure upd_sal(name varchar2,salary number) is
   begin
     if salary between minsal and maxsal then
        update emp set sal=salary where upper(ename)=upper(name);
        if sql%notfound then
            raise_application_error(-20004,'不存在該雇員名');
        end if;
     else
        raise_application_error(-20001,'工資不在范圍內');
     end if;
   end;
  begin
    select mi(sal),max(sal) into minsal,maxsal from emp ;
  end;

  調用包公用組件:構造過程只調用一次
  exec emp_package.add_employee(1111,'mary',3000,20)
  exec emp_package.upd_sal('mary',2000)
 


  8.使用純度級別
  在SQL中引用包的公用函數,該公用函數不能包含DML語句(insert,update,delete),也不能讀寫遠程包的變量
  為了對包的公用函數加以限制,在定義包規范時,可以使用純度級別(purity level)限制公用函數
  語法:pragma restrict_references (function_name,wnds[,wnps][,rnds][,rnps]);
  wnds:用於限制函數不能修改數據庫數據(禁止DML)
  wnps:用於限制函數不能修改包變量(不能給包變量賦值)
  rnds:用於限制函數不能讀取數據庫數據(禁止SELECT操作)
  rnps:用於限制函數不能讀取包變量(不能將包變量賦值給其他變量)

  1.建立包規范
  create or replace package purity is 
    minsal number(6,2);
    maxsal number(6,2);
    function max_sal return number;
    function min_sal return number;
    pragma restrict_references(max_sal,wnps);--不能修改
    pragma restrict_references(min_sal,wnps);
  end;
 
  2.建立包體
  create or replace package body purity is
   function max_sal return number
   is 
   begin
     return maxsal;
   end;
   
   function min_sal return number
   is
   begin
     return minsal;
   end;
   
   begin
     select min(sal),max(sal) into minsal,maxsal from emp;
   end;

   3.調用包的公用函數
   var minsal number
   var maxsal number
   exec :minsal:=purity.minsal()
   exec :maxsal:=purity.maxsal()
   print minsal maxsal

  


  12.觸發器
  存放在數據庫中,並被隱含執行的存儲過程.
  可基於表和視圖的DML(INSERT,UPDATE,DELETE),系統事件(啟動,關閉,登陸數據庫)和DDL操作建立觸發器.
  觸發器由觸發事件,觸發條件,觸發操作三部分組成
  1.1觸發事件
  啟動和關閉數據庫,ORACLE錯誤消息,用戶登錄和斷開會話,特定表或視圖的DML操作,在任何方案上的DDL語句 
  
  1.2.觸發條件(可選)
  指使用WHEN子句指定一個BOOLEAN表達式,返回為TRUE時,觸發
  
  1.3.觸發操作
  指包含SQL語句和其他執行代碼的PL/SQL塊.
  觸發器的代碼大小不能超過32K.(可使用CALL語句調用存儲過程)
  出發器只能包含SELECT,INSERT,UPDATE,DELETE語句,不能包含DDL語句(CREATE,ALTER,DROP)和事務控制語句(COMMIT,ROLLBACK,SAVEPOINT)

  
  2.建立DML觸發器
  當建立DML觸發器時,需要指定觸發時機(BEFORE,AFTER),觸發事件(INSERT,UPDATE,DELETE),表名,觸發類型,觸發條件,操作.
  2.1觸發時機
  指定觸發器的觸發時間,之前(BEFORE),之后(AFTER)
  2.2觸發事件
  指定導致觸發器執行的DML操作,也即INSERT,UPDATE,DELTE操作
  2.3表名
  必須指定DML操作所對應的表
  2.4觸發類型
  指定觸發事件發生后,需要執行幾次觸發操作,默認執行一次觸發器代碼
  如果指定行觸發類型,則會在每個被作用行上執行一次觸發器代碼
  2.5觸發條件
  指定執行觸發器代碼的條件
  當編寫DML觸發器時,只允許在行觸發器上指定觸發條件
  2.6觸發操作
  指定觸發器執行代碼.(若使用PL/SQL,JAVA,或外部存儲過程,可直接使用CALL調用相應過程)    
  2.7DML觸發器觸發順序
  DML觸發器在單行數據上的觸發順序
  對於單行數據而言,無論是語句觸發器,還是行觸發器,觸發器代碼實際只被執行一次,順序為:
  before語句,before行,dml操作,after行,after語句
  
  DML觸發器在多行數據上的觸發順序
  對於多行數據而言,語句觸發器只被執行一次,而行觸發器在每個作用行上都執行一次
 

  3.語句觸發器
  當執行DML操作時會自動執行觸發器的相應代碼.
  使用語句觸發器時,不能記錄列數據的變化
  語法:create [or replace] trigger trigger_name
       timing event1 [or event2 or event3]
       on table_name
       pl/sql block
  3.1建立BEFORE語句觸發器
  create or replace trigger tr_sec_emp
  before insert or update or delete on emp
  begin
    if to_char(sysdate,'DY','nls_date_language=AMERICAN') IN ('SAT','SUN') THEN
       raise_application_error(-20001,'不能在休息日改變雇員信息');
    end if;
  end;

  3.2使用條件謂詞:inserting,updating,deleting是該操作時返回TRUE,否則FALSE
  create or replace trigger tr_sec_emp
  before insert or update or delete on emp
  begin
    if to_char(sysdate,'DY','nls_date_language=AMERICAN') IN ('SAT','SUN') THEN
      CASE
        WHEN INSERTING THEN
          raise_application_error(-20001,'不能在休息日增加雇員');
        WHEN UPDATING THEN
          raise_application_error(-20002,'不能在休息日更新雇員');
        WHEN DELETING THEN
          raise_application_error(-20003,'不能在休息日解雇雇員');
      end case;
    end if;
  end;

  
  4.建立AFTER語句觸發器
  為了審計DML操作,或者在DML操作之后執行匯總運算.(計算INSERT,UPDATE,DELETE的操作次數)
  create table audit_table(name varchar2(20),ins int,upd int,del int,starttime date,endtime date);
  
  create or replace trigger tr_audit_emp
  after insert or update or delete on emp
  declare
    v_temp int;
  begin
    select count(*) into v_temp from audit_table where name='emp';
    if v_temp=0 then
       insert into audit_table values('emp',0,0,0,sysdate,null);
    end if;
    case
      when inserting then update audit_table set ins=ins+1,endtime=sysdate where name='emp';
      when updating then update audit_table set upd=upd+1,endtime=sysdate where name='emp';
      when deleting then update audit_table set del=del+1,endtime=sysdate where name='emp';
    end case;
  end;

  5.行觸發器
  執行DML操作時,每作用一行就觸發一次的觸發器.審計數據變化時,可以使用行觸發器
  create [or replace] trigger trigger_name
  timing event1 [or event2 or event3]
  on table_name
  [referencing old as old |new as new]
  for each row
  [when condition]
  pl/sql block;

  5.1建立BEFORE行觸發器
  create or relpace trigger tr_emp_sal
  before update of sal on emp
  for each row
  begin
    if :new.sal<:old.sal then
       raise_application_error(-20010,'工資只漲不降');
    end if ;
  end;

  5.2建立AFTER行觸發器
  為了審計DML操作數據變化,則應該使用AFTER行觸發器
  create table audit_emp_change(name varchar2(10),oldsal number(6,2),newsal number(6,2),time date);
  
  create or replace trigger tr_sal_change
  after update of sal on emp
  for each row
  declare
    v_temp int;
  begin
    select count(*) into v_temp from audit_emp_change where name=:old.ename;
      if v_temp=0 then
         insert into audit_emp_change values(:old.ename,:old.sal,:new,sal,sysdate);
      else
         update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
      end if;
  end;

  5.3限制行觸發器
  使用行觸發器時,默認情況下會在每個被作用行上執行一次觸發器代碼,為了使得在特定條件下執行行觸發器代碼,就需要使用WHEN子句對觸發器加以限制
  create or replace trigger tr_sal_change 
  after update of sal on emp
  for each row
  when (old.job='salesman')
  declare
    v_temp int;
  begin
    select count(*) into v_temp from audit_emp_change where name=:old.ename;
    if v_temp=0 then
       insert into audit_emp_change values(:old.ename,:old.sal,:new.sal,sysdate);
    else
       update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
    end if;
  end;

  5.4DML觸發器使用注意事項
  編寫DML觸發器時,不能從觸發器所對應的基表中讀取數據.建立時不會出現錯誤,但在執行相應觸發操作時會顯示錯誤信息
  
  6.使用DML觸發器
  DML觸發器可以用於實現數據安全保護,數據審計,數據完整性,參照完整性,數據復制等
  6.1控制數據安全
  在服務器級控制數據安全是通過授予和收回對象權限來完成的.
  conn scott/tiger
  grant select,insert,update,delete on emp to smith;

  create or replace trigger tr_emp_time
  before insert or update or delete on emp
  begin
    if to_char(sysdate,'hh24') not between '9' and '17' then
       raise_application_error(-20101,'非工作時間');
    end if;
  end;

6.2實現數據審計
  審計可以用於監視非法和可疑的數據庫活動.ORACLE數據庫本身提供了審計功能
  對EMP表上的DML操作進行審計
  audit insert,update,delete on emp by access; 
  只能審計SQL操作,寫入到數據字典中,而不會記載數據變化.為了審計SQL操作引起的數據變化,使用DML觸發器
  create or replace trigger tr_sal_change
  after update of sal on emp
  for each row
  declare
    v_temp int;
  begin
    select count(*) into v_temp from audit_emp_change where name=:old.ename;
    if v_temp=0 then
       insert into audit_emp_change values(:old.ename,:old.sal,:new.sal,sysdate);
    else
       update audit_emp_change set oldsal=:old.sal,newsal=:new.sal,time=sysdate where name=:old.ename;
    end if;
  end;

  6.3實現數據完整性
  數據完整性用於確保數據庫數據滿足特定的商業邏輯或企業規則.
  CHECK約束:alter table emp add constraint ck_sal check (sal>=800);
  create or replace trigger tr_check_sal
  before update of sal on emp
  for each row
  when (new.sal<old.sal or new.sal>1.2*old.sal)
  begin
    raise_application_error(-20931,'工資只升不降,並且升幅不能超過20%');
  end;

  6.4實現參照完整性
  指若兩個表之間具有主從關系(即主外鍵關系),當刪除主表數據時,必須確保相關的從表數據已經被刪除.
  當修改主表的主鍵列數據時,必須確保相關從表數據已經被修改.為了實現級聯刪除,可在定義外部鍵約束時指定ON DELETE CASCADE關鍵字
  alter table emp add constraint fk_deptno foreign key (deptno) references dept(deptno) on delete cascade;

  實現級聯更新,可以使用觸發器
  create or replace trigger tr_update_cascade
  after update of deptno on dept
  for each row
  begin
    update emp set deptno=:new.deptno where deptno=:old.deptno;
  end;

  7.建立INSTEAD OF觸發器
  對於簡單視圖可直接執行INSERT,UPDATE,DELETE操作,但對於復雜視圖,不允許直接執行DML操作
  具有集合操作符:UNION,UNION ALL,INTERSECT,MINUS
  具有分組函數:MIN,MAX,SUM,AVG,COUNT等
  具有GROUP BY ,CONNECT BY ,START WITH等子句
  具有DISTINCT關鍵字
  具有連接查詢
  為了在具有以上情況的復雜視圖上執行DML操作,必須要基於視圖建立INSTEAD-OF觸發器
  在建立了INSTEAD-OF觸發器之后,就可以基於復雜視圖執行INSERT,UPDATE,DELETE語句
  注意事項:
      INSTEAD OF選項只適用於視圖
      當基於視圖建立觸發器時,不能指定BEFORE和AFTER選項
      在建立視圖時沒有指定WITH CHECK OPTION選項
      當建立INSTEAD OF觸發器時,必須指定FOR EACH ROW選項
  
  7.1建立復雜DEPT_EMP
  create or replace view dept_emp as select a.deptno,a.dname,b.empno,b.ename from dept a,emp b where a.deptno=b.deptno;
  不允許執行DML操作
  
  7.2建立INSTEAD-OF觸發器
  為了在復雜視圖上執行DML操作,必須要基於復雜視圖來建立INSTEAD-OF觸發器
  create or replace trigger tr_instead_of_dept_emp
  instead of insert on dept_emp
  for each row
  declare
    v_temp int;
  begin
    select count(*) into v_temp from dept where deptno=:new.deptno;
    if v_temp=0 then
       insert into dept (deptno,dname) values(:new.deptno,:new.dname);
    end if;
    select count(*) into v_temp from emp where empno=:new.empno;
    if v_temp=0 then
       insert into emp(empno,ename,deptno) values(:new.empno,:new.ename,:new.deptno);
    end if;
  end;
  當建立INSTEAD-OF觸發器之后,就可以在復雜視圖DEPT_EMP上執行INSERT操作了
  insert into dept_emp values(50,'admin','1223','mary');
  insert into dept_emp values(10,'admin','1224','bake');

 


  8.建立系統事件觸發器
  基於ORACLE系統事件(logon,startup)所建立的觸發器,跟蹤系統或數據庫變化的機制
  
  8.1常用事件屬性函數
  ora_client_ip_address:返回客戶端IP地址
  ora_database_name:返回當前數據庫名
  ora_des_encrypted_password:用於返回DES加密后的用戶口令
  ora_dict_obj_name:用於返回DDL操作所對應的數據庫對象名
  ora_dict_obj_list(name_list out ora_name_list_t):返回在事件中被修改的對象名列表
  ora_dict_obj_owner:返回DDL操作所對應的對象的所有者名
  ora_dict_obj_owner_list(owner_list out ora_name_list_t):返回在事件中被修改對象的所有者列表
  ora_dict_obj_type:返回DDL操作所對應的數據庫對象的類型
  ora_grantee(user_list out ora_name_list_t):返回授權事件的授權者
  ora_instance_num:返回例程號
  ora_is_alter_column(column_name in varchar2):用於檢測特定列是否被修改
  ora_is_creating_nested_table:用於檢測是否正在建立嵌套表
  ora_is_drop_column(column_name in varchar2):用於檢測特定列是否被刪除
  ora_is_servererror(error_number):用於檢測是否返回特定oracle錯誤
  ora_login_user:返回登錄用戶名
  ora_sysevent:用於返回觸發觸發器的系統事件名

  8.2建立例程啟動和關閉觸發器(特權用戶)
  記載例程啟動和關閉的事件和時間
  conn sys/oracle as sysdba
  create table event_table(event varchar2(30),time date);
  
  create or replace trigger tr_startup
  after startup on database
  begin 
    insert into event_table values(ora_sysevent,sysdate);
  end;

  create or replace trigger tr_shutdown
  before shutdown on database
  begin
    insert into event_table values(ora_sysevent,sysdate);
  end;

  shutdown
  shutup
  
  8.3建立登錄和退出觸發器
  記載用戶登陸和退出事件.
  conn sys/oracle as sysdba
  create table log_table(username varchar2(20),logon_time date,logoff_time date,address varchar2(20));
  
  create or replace trigger tr_logon
  after logon on database
  begin
    insert into log_table (username,logon_time,address) values(ora_login_user,sysdate,ora_client_ip_address);
  end;
  
  create or replace trigger tr_logoff
  before logoff on database
  begin
    insert into log_table (username,logoff_time,address) values(ora_login_user,sysdate,ora_client_ip_address);
  end;
 
  conn scott/tiger@orc1
  conn system/manager@orc1
  conn sys/oracle@orc1 as sysdba
  
  8.4建立DDL觸發器  
  記載系統所發生的DDL事件(CREATE,ALTER,DROP等)
  conn sys/oracle as sysdba
  create table event_ddl(event varchar2(20),username varchar2(10),owner varchar2(10),objname varchar2(20),objtype varchar2(10),time date);

  create or replace trigger tr_ddl
  after ddl on scott.schema
  begin
    insert into event_ddl values(ora_sysevent,ora_login_user,ora_dict_obj_owner,ora_dict_obj_name,ora_dict_obj_type,sysdate);
  end;

  conn scott/tiger
  create table temp(cola int);
  drop table temp
  
  9.管理觸發器
  9.1顯示觸發器信息:在數據字典中USER_TRIGGERS
  conn scott/tiger
  select trigger_name,status from user_triggers where table_name='emp';
 
  9.2禁止觸發器
  使觸發器臨時失效,處於ENABLE狀態,則會觸發
  alter trigger tr_check_sal disable;

  9.3激活觸發器
  使觸發器重新生效
  alter trigger tr_check_sal enable;

  9.4禁止或激活表的所有觸發器
  alter table emp disable all triggers
  alter table emp enable all triggers
 
  9.5重新編譯觸發器
  使用ALTER TABLE命令修改表結構時,會使得觸發器轉變為INVALID狀態,需要重新編譯觸發器,才能繼續生效
  alter trigger tr_check_sal compile;
 
  9.6刪除觸發器
  drop trigger tr_check_sal
 
 

  10.開發動態SQL
  在PL/SQL塊中編寫動態SQL語句時,需要將SQL語句存放到字符串變量中,而且SQL可以包含占位符
  1.動態SQL處理方法
  1.1使用EXECUTE IMMEDIATE語句
  可處理DDL語句(CREATE,ALTER,DROP),DCL語句(GRANT,REVOKE),DML語句(INSERT,UPDATE,DELETE)以及單行SELECT語句 
  不能用於處理多行查詢語句
  
  1.2使用OPEN-FOR,FETCH和CLOSE語句
  為了處理動態的多行查詢操作,必須要使用OPEN-FOR語句打開游標,使用FETCH循環提取數據,CLOSE關閉游標
  
  1.3使用批量動態SQL
  
  2.處理非查詢語句:DDL,DCL,DML
  語法:execute immediate dynamic_string
       [into (define_variable[,define_variable]...|record)]
       [using [in|out|in out] bind_argument [,[in|out|in out] bind_argument]...]
       [(returning | return) into bind_argument[,bind_argument]...]

  2.1使用EXECUTE IMMEDIATE處理DDL操作
  EXECUTE IMMEDIATE后面只需要帶有DDL語句,不需要INTO 和USING子句
  
  create or replace procedure drop_table(table_name varchar2)
  is
    sql_statement varchar2(100);
  begin
    sql_statement:='drop table '||table_name;
    execute immediate sql_statement;
  end;

  exec drop_table('worker');
  
  2.2使用EXECUTE IMMEDIATE處理DCL操作 
  conn system/manager
  create or replace procedure grant_sys_priv(priv varchar2,username varchar2)
  is
    sql_stat varchar2(100);
  begin
    sql_stat:='grant '||priv||' to '||username;
    execute immediate sql_stat;
  end;

  exec grant_sys_priv('create session','scott')

  2.3使用EXECUTE IMMEDIATE處理DML操作
  如果DML語句包含有占位符,那么在EXECUTE IMMEDIATE語句之后必須要帶有USING子句
  如果DML語句帶有RETURNING子句,那么在EXECUTE IMMEDIATE語句之后需要帶有RETURNING INTO子句
  
  2.4處理無占位符和RETURNING子句的DML語句
  declare
    sql_stat varchar2(100);
  begin
    sql_stat:='update emp set sal=sal*1.1 where deptno=30';
    execute immediate sql_stat;
  end;

  2.5處理包含占位符的DML語句:要使用USING子句
  declare
    sql_stat varchar2(100);
  begin
    sql_stat:='update emp set sal=sal*(1+:percent/100)'||' where deptno=:dno';
    execute immediate sql_stat using &1,&2;
  end;

  2.6處理包含RETURNING子句的DML語句:必須使用RETURNING INTO子句接受返回數據
  當直接使用EXECUTE IMMEDIATE處理帶有RETURNING子句的DML時,只能處理作用在單行上的DML語句
  如果DML語句作用在多行上,則必須要使用BULK子句
  declare
    salary number(6,2);
    sql_stat varchar2(100);
  begin
    sql_stat:='update emp set sal=sal*(1+percent/100)'||' where empno=:eno returning sal into :salary';
    execute immediate sal_stat using &1,&2 returning into salary;
    dbms_output.put_line('新工資:'||salary);
  end;

  2.7使用EXECUTE IMMEDIATE處理單行查詢:需要使用INTO子句接受返回數據
  declare
    sql_stat varchar2(100);
    emp_record emp%rowtype;
  begin
    sql_stat:='select * from emp where empno=:enp';
    execute immediate sql_stat into emp_record using &1;
    dbms_output.put_line('雇員'||emp_record.ename||'的工資為'||emp_record.sal);
  end;

  
  3.處理多行查詢語句 
  使用EXECUTE IMMEDIATE只能處理單行查詢語句.
  為了動態的處理SELECT語句所返回的多行數據,需要使用OPEN-FOR ,FETCH ,CLOSE語句 
 
  3.1定義游標變量
  動態處理多行查詢需要使用游標變量來完成
  type cursortype is ref cursor;
  cursor_variable cursortype;
  3.2打開游標變量
  open cursor_variable for dynamic_string [using bind_argument[,bind_argument]...];
  3.3循環提取數據
  fetch cursor_variable into {var1[,var2]...|record_var};
  3.4關閉游標變量
  close cursor_variable
  3.5多行查詢示例
  declare
    type empcurtyp is ref cursor;
    emp_cv empcurtyp;
    emp_record emp%rowtype;
    sql_stat varchar2(100);
  begin
    sql_stat:='select * from emp where deptno=:dno';
    open emp_cv for sql_stat using &dno;
    loop
      fetch emp_cv into emp_record;
      exit when emp_cv%notfound;
      dbms_output.put_line('雇員名:'||emp_record.ename||',工資:'||emp_record.sal);
    end loop;
    close emp_cv;
  end;


  4.在動態SQL中使用BULK子句
  三種語句支持BULK子句:EXECUTE IMMEDIATE,FETCH,FORALL
  1在EXECUTE IMMEDIATE語句中使用動態BULK子句(處理多行查詢語句)
  execute immediate dynamic_string
  [bulk collect into define_variable[,define_variable...]]
  [using bind_argument[,bind_argument...]]
  [[returning | return]
  bulk collect into return_variable[,return_variable...]];

  1.1使用BULK子句處理DML語句返回子句
  declare
    type ename_table_type is table of emp.ename%type index by binary_integer;
    type sal_table_type is table of emp.sal%type index by binary_integer;
    ename_table ename_table_type;
    sal_table sal_table_type;
    sql_stat varchar2(100);
  begin
    sql_stat:='update emp set sal=sal*(1+:percent/100)'||' where deptno=:dno'||' returning ename,sal into :name,:salary';
    execute immediate sql_stat using &percent,&dno returning bulk collect into ename_table,sal_table;
    for i in 1..ename_table.count loop
      dbms_output.put_line('雇員'||ename_table(i)||'的新工資為'||sal_table(i));
    end loop;
  end;

  1.2使用BULK子句處理多行查詢
  declare
    type ename_table_type is table of emp.ename%type index by binary_integer;
    ename_table ename_table_type;
    sql_stat varchar2(100);
  begin
    sql_stat:='select ename from emp where deptno=:dno';
    execute immediate sql_stat bulk collect into ename_table using &dno;
    for i in 1..ename_table.count loop
      dbms_output.put_line(ename_table(i));
    end loop;
  end;

  2.在FETCH語句中使用BULK子句
  默認情況下FETCH語句每次只能提取單行數據,為了處理所有數據,需要使用循環語句.通過引入BULK,一次就可以提取所有數據
  fetch dynamic_cursor bulk collect into define_variable[,define_variable...];

  declare
    type empcurtyp is ref cursor;
    emp_cv empcurtyp;
    type ename_table_type is table of emp.ename%type index by binary_integer;
    ename_table ename_table_type;
    sql_stat varchar2(100);
  begin
    sql_stat:='select ename from emp where job=:title';
    open emp_cv for sql_stat using '&job';
    fetch emp_cv bulk collect into ename_table;
    for i in 1..ename_table.count loop
      dbms_output.put_line(ename_table(i));
    end loop;
    close emp_cv;
  end;

 
  3.在FORALL語句中使用BULK子句
  FORALL只使用於動態的INSERT,UPDATE,DELETE語句,而不適合於動態的SELECT語句,並且FORALL語句要和EXECUTE IMMEDIATE語句結合使用
  forall index in lower bound..upper bound
     execute immediate dynamic_string
     using bind_argument |bind_argument(index)
     [,bind_argument| bind_argument(index)]...
     [(returning | return) bulk collect into bind_argument[,bind_argument...]];

  declare
    type ename_table_type is table of emp.ename%type;
    type sal_table_type is table of emp.sal%type;
    ename_table ename_table_type;
    sal_table sal_table_type;
    sql_stat varchar2(100);
  begin
    ename_table:=ename_table_type('scott','smith','clark');
    sql_stat:='update emp set sal=sl*1.1 where ename=:1'||' returning sal into :2';
    forall i in 1..ename_table.count
       execute immediate sql_stat using ename_table(i) returning bulk collect into sal_table;
    for j in 1..ename_table.count loop
      dbms_output.put_line('雇員'||ename_table(j)||'的新工資'||sal_table(j));
    end loop;
 end;

 


  11.對象類型
  1.1對象類型組件
  對象類型包括屬性和方法.
  1.2對象類型和對象實例
  對象實例OBJECT INSTANCE是對象類型的具體實現,對應於現實世界的具體對象
  1.3構造對象類型
  對象類型包括對象類型規范和對象類型體兩部分
  1.4對象類型屬性
  必須要提供屬性名和數據類型
  對象類型屬性不能使用以下數據類型:LONG,LONG RAW,ROWID,UROWID,PL/SQL特有類型,不能指定對象屬性的默認值,也不能指定NOT NULL選項
  1.5對象類型方法
  用於描述對象所要執行的操作
  定義方法:可以定義構造方法,MEMBER方法,STATIC方法,MAP方法以及ORDER方法
  
  構造方法CONSTRUCT METHOD:
  用於初始化對象並返回對象實例,類似於JAVA語言的構造方法
  構造方法是與對象類型同名的函數,其默認參數是對象類型的所有屬性
  
  MEMBER方法
  用於訪問對象實例的數據
  當使用MEMBER方法時,可以使用內置參數SELF訪問當前對象實例
  只能由對象實例調用,而不能由對象類型調用.
  object_type_instance.method();

  STATIC方法:
  用於訪問對象類型,在對象類型上執行全局操作,而不需要訪問特定對象實例的數據
  只能由對象類型調用,而不能由對象實例調用
  
  MAP方法:
  是對象類型的一種可選方法,可以將對象實例影射為標量類型數據(DATE,NUMBER,VARCHAR2),然后根據該標量類型數據可以排序對象實例,對象類型最多只能定義一個MAP方法
  
  ORDER方法:
  MAP方法可以在多個對象實例之間進行排序,而ORDER方法只能比較兩個對象實例的大小.
  定義對象類型時,最多只能定義一個ORDER方法
  MAP方法和ORDER方法不能同時定義,如果不需要比較對象實例,則不需要定義MAP和ORDER方法
 

  2.對象表OBJECT TABLE
  指包含對象類型列的表,對象表至少會包含一個對象類型列.
  ORACLE包含行對象和列對象兩種對象表     
  行對象是指直接基於對象類型所建立的表,而列對象則是包含多個列的對象表
  行對象:create table employee of employee_type;
  列對象:create table department(
                dno number,dname varchar2(10),
                employee employee_type);
  3.對象類型繼承:TYPE INHERITANCE
  指一個對象類型繼承另一個對象類型
  
  4.REF數據類型
  REF是指向行對象的邏輯指針,是ORACLE的一種內置數據類型
  建表時通過使用REF引用行對象,可以使不同表共享相同對象,從而降低內存占用.
  ref table department(dno number(2),dname varchar2(10),emp ref employee_type);

  11.2建立和使用簡單對象類型
  建立對象類型規范的語法:
  create or replace type type_name as object(
    attribute1 datatype[,attribute2 datatype,...],
    [member|static method1 spec,member|static method2 spec,..]);

  建立對象類型體的語法:
  create or replace type body type_name as 
    member|static method1 body;
    member|static method2 body;
    ....]

  1.建立和使用不包含任何方法的對象類型
  對象類型可以不包含任何方法
  create or replace type person_type1 as object(
    name varchar2(10),gender varchar2(2),birthdate date);

  1.1建立行對象
  行對象是指直接基於對象類型所建立的表
  create table person_tab1 of person_typ1;

  為行對象插入數據
  begin
    insert into person_tab1 values('馬麗','女','11-1月-76');
    insert into person_tab2 values(person_type1('王名','男','11-12月-76);--使用對象類型的構造方法來插入數據
  end;

  檢索行對象數據
  必須要使用函數VALUE取得行對象數據,並檢索到對象類型變量中
  declare
    person person_typ1;
  begin
    select value(p) into person from person_tab1 p where p.name='&name';
    dbms_output.put_line('性別:'||person.gender);
    dbms_output.put_line('出生日期:'||person.birthdate);
  end;

  更新行對象數據 
  如果按照對象屬性更新數據,則必須要為行對象定義別名.
  begin 
    update person_tab1 p set p.birthdate='11-2月-76' where p.name='馬麗';
  end;
 
  刪除行對象數據
  如果按照對象屬性刪除數據,則必須要為行對象定義別名
  begin
    delete from person_tab1 p where p.name='馬麗';
  end;

  1.2建立列對象
  create table employee_tab1(
    eno number(6),person person_typ1,sal number(6,2),job varchar2(10));

  為列對象employee_tab1插入數據
  begin 
    insert into empoyee_tab1(eno,sal,job,person) value1,2000,'高級鉗工',person_typ1('王名','男','11-1月-76');
  end;

  檢索對象類型列的數據
  檢索列對象的對象類型列數據時可以直接將對象實例數據檢索到對象類型變量中
  declare
    employee person_typ1;
    salary number(6,2);
  begin
    select person,sal into employee,salary from employee_tab1 where eno=&no;
    dbms_output.put_line('雇員名:'||employee.name);
    dbms_output.put_line('雇員工資:'||salary);
  end;

  更新對象列數據
  更新列對象的對象列數據時必須要為列對象定義別名,並且引用對象屬性(列對象別名.對象類型列名.對象屬性名)
  begin
    update employee_tab  p set p.person.birthdate='&newdate' where p.person.name='&name';
  end;

  依據對象屬性刪除數據
  依據對象屬性刪除列數據時必須要為對象表定義別名,並且引用對象屬性(列對象別名.對象類型列名.對象屬性名)
 


  2.建立和使用包含MEMBER方法的對象類型
  MEMBER方法用於訪問對象實例的數據,如果在對象類型中需要訪問特定對象實例的數據,則必須要定義MEMBER方法.
  MEMBER方法只能由對象實例調用,而不能由對象類型調用.
  create or replace type person_typ2 as object(
    name varchar2(10),gender varchar2(2),biethdate date,address varchar2(100),
    member procedure change_address(new_addr varchar2),
    member function get_info feturn varchar2);

  在建立類型規范時定義了MEMBER方法,所以必須要通過對象類型體實現這些方法.
  create or replace type body person_typ2 is  
    member procedure change address(new_addr varchar2)
    is 
    begin
      address:=new_addr;
    end;
    member function get_info return varchar2
    is 
      v_info varchar2(100);
    begin
      v_info:='姓名'||name||',出生日期:'||birthdate;
      return v_info;
    end;
  end;

  使用對象類型
  create table employee_tab2(
    eno number(6),person person_typ2,sal number(6,2),job varchar2(10));
    insert into employee_tab2(eno,sal,job,person) vaules(1,2000,''高級焊工',person_typ2('王名','男','11-1月-76','福州市'));
    insert into employee_tab2(eno,sal,job,person) values(2,1500,'質量檢查員',person_typ2('馬麗','女','11-1月-76','福州市'));

  調用對象方法
  declare
    v_person person_typ2;
  begin
    select person into v_person from employee_tab2 where eno=&&no;
    v_person.change_address('福清');
    update employee_tab2 set person=v_person where eno=&no;
    dbms_output.put_line(v_person.get_info);
  end;

  3.建立和使用包含STATIC方法的對象類型
  static方法用於訪問對象類型,在對象類型上執行全局操作
  STATIC方法只能由對象類型訪問,不能由對象實例訪問
  建立類型規范:
  create or replace type person_typ3 as object(
    name varchar2(10),gender varchar2(2),
    birthdate date,regdate date,
    static function getdate return date,
    member function get_info return varchar2);
  
  建立對象類型體:
  create or replace type body person_typ3 is 
    static function getdate return date is 
    begin
      return sysdate;
    end;
    member function get_info return varchar2
    is
    begin
      return '姓名:'||name||',注冊日期:'||regdate;
    end;
  end;

  使用對象類型及其STATIC方法和MEMBER方法
  create table employee_tab3(
    eno number(6),person person_typ3,sal number(6,2),job varchar2(10));
  
  在對象類型上使用STATIC方法:對象類型調用
  begin
    insert into employee_tab3(eno,sal,job,person) values(&no,&salary,'&title',person_typ3("&name','&sex','&birthdate',person_typ3.getdate()));

  使用MEMBER方法:實例對象調用
  declare
    v_person person_typ3;
  begin
    select person into v_person from employee_tab3 where eno=&no;
    dbms_output.put_line(v_person.get_info());
  end;

  
  4.建立和使用包含MAP方法的對象類型
  MAP方法用於將對象實例映射為標量數值(NUMBER,DATE,VARCAHR2等)
  為了排序多個對象實例的是數據,可以在建立對象類型時定義MAP方法 
  一個對象類型最多只能定義一個MAP方法,並且MAP方法和ORDER方法不能同時使用
  建立對象類型規范:
  create or replace type person_typ4 as object(
    name varchar2(10),gender varchar2(2),birthdate date,
    map member function getage return varchar2);

  建立對象類型體:
  create or replace type body person_typ4 is 
    map member function getage return varchar2
    is
    begin
      return trunc((sysdate-birthdate)/365);
    end;
  end;

  使用對象類型和方法:
  create table employee_tab4(
    eno number(6),person person_typ4,sal number(6,2),job varchar2(10));
  insert into employee_tab4(eno,sal,job,person) values (1,1500,'圖書管理員',person_typ4('馬麗','女','11-1月-76'));
  insert into employee_tab4(eno,sal,job,person) values (2,1500,'圖書管理員',person_typ4('馬麗','女','11-1月-76')); 
  insert into employee_tab4(eno,sal,job,person) values (3,1500,'圖書管理員',person_typ4('馬麗','女','11-1月-76'));
  
  使用MAP方法getage比較對象實例的方法
  declare
    type person_table_type is table of person_typ4;
    person_table person_table_type;
    v_temp varchar2(100);
  begin
    select person bulk collect into person_table from employee_tab4;
    if person_table(1).getage()>person_table(2).getage() then
       v_temp:=preson_table(1).name||'比'||person_table(2).name||'大';
    else
       v_temp:=preson_table(1).name||'比'||person_table(2).name||'小';
    end if;
    dbms_output.put_line(v_temp);
  end;

  
  5.建立和使用包含ORDER方法的對象類型
  order方法用於比較兩個對象實例的大小.
  一個對象類型最多只能包含一個ORDER方法,並且OREDER方法不能與MAP方法同時使用
  建立對象類型規范:
  create or replace type person_typ5 as object(
    name varchar2(10),gender varchar2(2),birthdate date,
    order member functon compare( p person_typ5) return int);

  建立對象類型體:
  create or replace type body person_typ5 is 
    order member function compare(p person_typ5) return int 
    is 
    begin
      case
        when birthdate>p.birthdate then return 1;
        when birthdate=p.birthdate then return 0;
        when birthdate<p.birthdate then return -1;
      end case;
    end;
  end;

  使用對象類型及其方法
  create table employee_tab5(
    eno number(6),person person_typ5,sal number(6,2),job varchar2(10));
  insert into employee_tab5(eno,sal,job,person) values(1,1500,'圖書管理員',person_typ5('馬麗','女','11-1月-76));
  insert into employee_tab5(eno,sal,job,person) values(2,1500,'圖書管理員',person_typ5('馬麗','女','11-1月-76));
  
  使用ORDER方法比較不同對象實例的數據
  declare
    type person_table is table of person_typ5;
    person_table person_table_type;
    v_temp varchar2(100);
  begin
    select person bulk collect into person_table from employee_tab5;
    if person_table(1),compare(person_table(2))=1 then
       v_temp:=person_table(1).name||'比'||person_table(2).name||'大';
    else
       v_temp:=person_table(1).name||'比'||person_table(2).name||'小';
    end if;
    dbms_output.put_line(v_temp);
  end;


  6.建立和使用自定義構造方法的對象類型
  當自定義構造方法時,構造方法的名稱必須要與對象類型的名稱完全相同,並且必須要使用CONSTRUCTOR FUNCTION關鍵字定義構造方法
  建立對象類型以及構造方法:
  create or replace type person_typ6o as object(
    name varchar2(10),gender varchar2(2),birthdate date,
    constructor function person_typ6(name varchar2) return selfas result,
    constructor function person_typ6(name varchar2,gender varchar2) return self as result,
    constructor function person_typ6(name varchar2,gender varchar2,birthdate date) return self as result);
  
  建立對象類型體實現其構造方法:
  create or replace type body person_typ6 is
    constructor function person_typ6(name varchar2) return self as result
    is 
    begin
      self.name:=name;
      self.gender:='女';
      self.birthdate:=sysdate;
      return;
    end;
    constructor function person_typ6(name varchar2,gender varchar2) return self as result
    is
    begin
      self.name:=name;
      self.gender:=gender;
      self.birthdate:=sysdate;
      return;
    end;
    constructor function person_typ6(name varchar2,gender varchar2,birthdate date) return self as result
    is
    begin
      self.name:=name;
      self.gender:=gender;
      self.birthdate:=birthdate;
      return;
    end;
  end;

  使用各種構造方法為其插入數據:
  create table employee_tab6(eno number(6),person person_typ6,sal number(6,2),job varchar2(10));
    insert into employee_tab6(eno,sal,job,person) values(1,1500,'圖書管理員',person_typ6('馬麗'));
    insert into employee_tab6(eno,sal,job,person) values(1,1500,'圖書管理員',person_typ6('馬麗','男'));
    insert into employee_tab6(eno,sal,job,person) values(1,1500,'圖書管理員',person_typ6('馬麗'.'男','11-1月-76'));
 

  12.3.建立和使用復雜對象類型
  復雜對象類型是指與其他對象類型具有關聯關系的對象類型
  1.對象類型嵌套
  建立對象類型
  create or replace type addr_typ7 as object(
    state varchar2(20),city varchar2(20),
    street varchar2(50),zipcode varchar2(6),
    member function get_addr return varchar2);
 
  建立對象類型體實現該方法:
  create or replace type body addr_typ7 as
    member function get_addr return varchar2 is
    begin
      return state||city||street;
    end;
  end;

  建立對象類型(嵌套):
  create or replace type person_typ7 as object(
    name varchar2(10),gender varchar2(2),birthdate date,address addr_typ7,
    member function get_info return varchar2);

  建立對象類型體:
  create or replace type body person_typ7 as
    member fucntion get_info return varchar2
    is
    begin
      return '姓名:'||name||',家庭住址:'||address.get_addr();
    end;
  end;

  建立操縱對象表:
  create table employee_tab7(
    eno number(6),person person_typ7,sal number(6,2),job varchar2(10));
  
  操縱對象表
  在PLS/SQL塊中為對象表插入數據
  begin
    insert into employee_tab7(eno,sal,job,person) values(1,1500,'圖書管理員',person_typ7('馬麗','女','11-1月-76',addr_typ7('內蒙古','呼和浩特','囫圇路10','010010')));
    insert into employee_tab7(eno,sal,job,person) values(2,1500,'圖書管理員',person_typ7('馬麗','女','11-1月-76',addr_typ7('內蒙古','呼和浩特','囫圇路20','010010')));
  end;

  在PL/SQL塊中更新對象列數據
  declare
    v_pserson person_typ7;
  begin
    select person into v_person from employee_tab7 where eno=&&no;
    v_person.address.street:='&street';
    update employee_tab7 set person=v_person where eno=&no;
  end;

  在PL/SQL塊中檢索對象列數據
  declare
    v_person person_typ7;
  begin
    select person into v_person from employee_tab7 where eno=&no;
    dbms_output.put_line(v_person.get_info);
  end;

  在PL/SQL塊中刪除對象表數據
  begin
    delete from employee_tab7 where eno=&no;
  end;

  2.參照對象類型
  指在建立對象表時使用REF定義表列,REF實際是指向行對象數據的邏輯指針
  通過使用REF定義表列,可以使得一個對象表引用另一個對象表(行對象)的數據,從而節省磁盤空間和內存空間
  建立對象類型:
  create or replace type person_typ8 as object(
    name varchar2(10),gender varchar2(2),birchdate date,address varchar2(50),
    member function get_info return varchar2);

  建立對象類型體:
  create or replace type body person_typ8 as
    member function get_info return varchar2
    is 
    begin
      return '姓名:'||name||',家庭住址:'||address;
    end;
  end;

  建立行對象並追加數據
  create table person_tab8 of person_typ8;
    insert into person_tab8 values('馬麗','女','11-1月-76','內蒙古呼和浩特10號');
    insert into person_tab8 values('王名','男','11-1月-76','內蒙古呼和浩特11號');
 

  對象表引用對象表(REF)
  建立對象表(引用表)
  create table employee_tab8(
    eno number(6),person ref person_typ8,sal number(6,2),job varchar2(10));
  
  操縱對象表
  為對象表出入數據 
  引用行對象表時,需要使用函數REF(),其返回值實際是指向相應數據行的指針
  begin
    insert into employee_tab8 select 1,ref(a),2000,'圖書管理員' from person_tab8 a where a.name = '馬麗';
    insert into employee_tab8 select 1,ref(a),2000,'圖書管理員' from person_tab8 a where a.name = '王名';
  end;

  檢索REF對象列數據 
  為了取得行對象的相應數據,必須要使用DEREF函數取得REF列所對應的實際數據
  declare
    v_person person_typ8;
  begin
    select deref(person) into v_person from employee_tab8 where eno=&no;
    dbms_output.put_line(v_person.get_info);
  end;

  更新REF對象列數據
  要修改其列所引用的數據,就必須修改相應的行對象
  declare
    v_person person_typ8;
  begin
    select deref(person) into v_person from employee_tab8 where eno=&no;
    v_person.address:='&address';
    update person_tab8 set address=v_person.address where name=v_person.name;
  end;

  刪除對象表數據
  begin
    delete from employee_tab8 where eno=&no;
  end;

 

  3.對象類型繼承
  指一個對象類型繼承另一個對象類型,並且對象類型繼承由父類型和子類型組成
  父類型用於定義不同對象類型的公用屬性和方法,而子類型不僅繼承了父類型的公用屬性和方法,而且還可具有自己的私有屬性和方法.
  使用對象類型繼承時,在定義父類型時必須要指定NOT FINAL 選項,如果不指定該選項,默認為FINAL,表示該對象類型不能被繼承
  
  建立父對象類型:
  create or replace type person_typ9 as object(
    name varchar2(10),gender varchar2(2),birthdate date,address varchar2(50),
    member function get_info return varchar2) not final;

  建立對象類型體:
  create or replace type body person_typ9 as 
    member function get_info return varchar2
    is
    begin
      return '姓名:'||name||',家庭住址:'||address;
    end;
  end;

  建立子對象類型(繼承):
  create or replace type employee_typ9 under person_typ9(
   eno number(6),sal number(6,2),job varchar2(10),
   member function get_other return varchar2);

  建立對象類型體(私有方法):
  create or replace type body employee_typ9 as 
    member function get_other return varchar2
    is
    begin
      return '雇員名稱:'||name||',工資:'||sal;
    end;
  end;

  建立對象表並插入數據
  sql>create table employee_tab9 of employee_typ9;
  sql>insert into employee_tab9 values('馬麗','女','01-11月-76','呼和浩特15號',2,2000,'高級鉗工');
  sql>insert into employee_tab9 values('王名','男','01-11月-76','呼和浩特15號',2,2000,'高級鉗工');    
  
  使用對象方法輸出數據
  declare
    v_employee employee_typ9;
  begin
    select value(a) into v_employee from employee_tab9 a where a.eno=&no;
    dbms_output.put_line(v_employee.get_info);
    dbms_output.put_line(v_employee.get_other);
  end;
 
  
  4.維護對象類型
  顯示對象類型信息  執行CREATE TYPE 命令建立對象類型時,ORACLE會將對象類型的信息存放到數據字典中(USER_TYPES)
  select type_name,attributes,final from user_types;
  desc person_typ1

  增加和刪除對象類型屬性
 如果已經基於對象類型建立了對象類型或對象表,那么在對象類型增加或刪除屬性時必須要帶有CASCADE關鍵字
  alter type preson_typ1 add attribute address varchar2(50) cascade
  alter type person_typ1 drop attrbute birthdate cascade;

  增加和刪除對象類型方法
  alter type person_typ1 add member function get_info return varchar2 cascade;
  create or replace type body person_typ1 as
    member function get_info return varchar2 is
    begin
      return '雇員名:'||name||',家庭住址:'||address;
    end;
  end;

 

  13.處理例外


  1.例外分類:預定義例外,非預定義例外,自定義例外三種
  2.例外處理:
  傳遞例外:如果在例外處理部分EXCEPTON沒有捕捉例外,ORACLE會將例外傳遞到調用環境.
  捕捉並處理例外:使用例外處理部分完成
  exception
    when exception1 [or exception2...] then
    statement1;
    statement2;
    .....
    when ....
    ...
    when others then    --必須是例外處理部分的最后一條子句
    statement1;
    ...
   
  2.處理預定義例外
  常用預定義例外
  2.1access_into_null:ora-06530錯誤.在引用對象屬性之前,必須首先初始化對象,否則觸發例外
  2.2case_not_found:ora-06592.在編寫CASE語句時,如果在WHEN子句中沒有包含必須的條件分支,並且沒有包含ELSE子句,就會觸發
  undef no
  declare
    v_sal emp.sal%type;
  begin
   select sal into v_sal from emp where empno=&&no;
   case 
     when v_sal<1000 then
       update emp set sal=sal+100 where empno=&no;
     when v_sal<2000 then
       update emp set sal=sal+150 where empno=&no;
     when v_sal<3000 then
       update emp set sal=sal+200 where empno=&no;
   end case;
   exception
     when case_not_found then
     dbms_output.put_line('在CASE語句中缺少與'||v_sal||'相關的條件');
   end;

  2.3collection_is_null:ora-06531
  在給集合元素(嵌套表和VARRAY類型)賦值前,必須首先初始化集合元素,否則觸發例外
  declare
    type ename_table_type is table of emp.ename%type;
    ename_table ename_table_type;
  begin
    select ename into ename_table(2) from emp where empno=&no;
    dbms_output.put_line('雇員名:||ename_table(2);
    exception
      when collection_null then
        dbms_output.put_line('必須使用構造方法初始化集合元素');
  end;
 
  2.4cursor_already_open:ora-06511
  當重新打開已經打開的游標時,會隱含的觸發例外.已經使用OPEN打開了顯示游標,並執行FOR循環,就會隱含的觸發該例外
  declare
    cursor emp_cursor is select ename,sal from emp;
  begin
    open emp_cursor;
    for emp_record in emp_cursor loop
      dbms_output.put_line(emp_record.ename);
    end loop;
    exception
      when cursor_already_open then 
      dbms_output.put_line('游標已打開');
  end;

  2.5DUP_VAL_ON_INDEX:ORA-00001
  當在唯一索引所對應的列上鍵入重復值時,觸發例外
  begin
    update dept set deptno=&new_no where deptno=&old_no;
    exception
      when dup_val_on_index then
        dbms_output.put_line('在DEPTNO列上不能出現重復值');
  end;

  2.6invalid_cursor:ora-01001
  當試圖在不合法的游標上執行操作時,會隱含的觸發例外.要從未打開的游標提取數據,或者關閉未打開的游標,則觸發例外
  declare
    cursor emp_cursor is select ename,sal from emp;
    emp_record emp_cursor%rowtype;
  begin
    fetch emp_cursor into emp_record;
    close emp_cursor;
  exception
    when invalid_cursor then
      dbms_output.put_line('請檢查游標是否已經打開');
  end;

  2.7invalid_number:ora-01722
  當內嵌SQL語句不能有效的將字符轉變成數字時,會隱含觸發例外
  begin
    update emp set sal=sal+'1oo';
  exception
    when invalid_number then
      dbms_output.put_line('輸入的數字值不正確');
  end;
 
  2.8no_date_found:ora-01403
  當執行SELECT INTO 未返回行,或者引用了索引表未初始化的元素時,會隱含觸發例外
  declare
    v_sal emp.sal%type;
  begin
    select sal into v_sal from emp where lower(ename)=lower('&name');
  exception
    when no_data_found then
      dbms_output.put_line('不存在該雇員');
  end;
  
  2.9too_many_rows:ora-01422
  當執行select into 語句時,如果返回超過一行,則會觸發例外
  declare
    v_ename emp.ename%type;
  begin
    select ename into v_ename from emp where sal=&sal;
  exception
    when too_many_rows then
      dbms_output.put_line('返回多行');
  end;

  2.10zero_divide:ora-01476
  如果使用數字值除0,則會隱含觸發例外
  declare
    num1 int:=100;
    num2 int:=0;
    num3 number(6,2);
  begin 
    num3:=num1/num2;
  exception 
    when zero_divide then
      dbms_output.put_line('分母不能為0');
  end;

  2.11subscript_beyond_count:ora-06533
  當使用嵌套表或VARRAY元素時,如果元素下標超出了嵌套表或VARRAY元素的范圍,則回隱含的觸發例外
  declare
    type emp_array_type is varray(20) of varchar2(10);
    emp_output.put_line(emp_array(3));
  begin
    emp_array:=emp_array_type('scott','maray');
    dbms_output.put_line(emp_array(3));
  exception
    when subscript_beyond_count then
      dbms_output.put_line('超出下標范圍');
  end;

  2.12subscript_outside_limit:ora-06532
  當使用嵌套表或VARRAY元素時,如果元素下標為負值,則會隱含觸發例外
  declare
    type emp_array_type is varray(20) of varray2(10);
    emp_array emp_array_type;
  begin
    emp_array:=emp_array_type('scott','mary');
    dbms_output.put_line(emp_array(-1);
  exception
    when subscript_outside_limit then
      dbms_output.put_line('嵌套表和VARRAY下標不能為負值');
  end;

  2.13value_error:ora-06502
  如果變量長度不足以容納實際數據,則會隱含的出發例外
  declare
    v_ename varchar2(5);
  begin
    select ename into v_ename from emp where empno=&no;
    dbms_output.put_line(v_ename);
  exception
    when value_error then
      dbms_output.put_line('變量尺寸不足');
  end;

  2.14其他預定義例外
  login_denied:ora-01017連接數據庫時,提供了不正確的用戶名和口令
  not_logged_on:ora-01012沒有連接到數據庫
  program_error:ora-06501存在PL/SQL內部問題,可能需要重新安裝數據字典和PL/SQL系統包
  rowtype_mismatch:ora-06504宿主游標變量和PL/SQL游標變量的返回類型不兼容
  self_is_null:ora-30625使用對象類型時,如果在NULL實例上調用成員方法,則會隱含觸發例外
  storage_error:ora-06500如果超出內存或者內存被損壞
  sys_invalid_rowid:ora-01410當字符串轉變為ROWID時,必須使用有效的字符串,否則觸發例外
  timeout_on_resource:ora-00051ORACLE在等待資源時出現超時錯誤

  3.處理非預定義例外
  使用預定義例外,只能處理21個ORACLE錯誤.
  使用非預定義例外包括三步:
  在定義部分定義例外名,然后在例外和ORACLE錯誤之間建立關聯,最終在例外處理部分捕捉並處理例外.
  當定義ORACLE錯誤和例外之間的關聯關系時,需要使用偽過程EXCEPTION_INIT
  declare
    e_integrity exception;
    pragma exception_init(e_integrity,-2291);
  begin
    update emp set deptno=&dno where empno=&eno;
  exception 
    when e_integrity then
      dbms_output.put_line('該部門不存在');
  end;

 

  4.處理自定義例外
  自定義例外與ORACLE錯誤沒有任何關聯
  與預定義和非預定義不同,自定義例外必須顯示觸發
  declare
    e_integrity exception;
    pragma exception_init(e_integrity,-2291);
    e_no_employee exception;
  begin
    update emp set deptno=&dno where empno=&eno;
    if sql%notfound then
       raise e_no_employee;
    end if;
    exception
      when e_integrity then
        dbms_output.put_line('該部門不存在');
      when e_no_employee then
        dbms_output.put_line('該雇員不存在');
  end;

 


  5.使用例外函數
  函數SQLCODE用於取得ORACLE錯誤號,而SQLERRM則用於取得與之相關的錯誤信息
  通過在存儲過程.函數.包中使用RAISE_APPLICATION_ERROR可以自定義錯誤號和錯誤消息
  5.1SQLCODE和SQLERRM
  undef v_sal
  declare
    v_ename emp.ename%type;
  begin
    select ename into v_ename from emp where sal=&&v_sal;
    dbms_output.put_line('雇員名:'||v_ename);
  exception
    when no_data_found then
      dbms_output.put_line('不存在工資為:'||&v_sal||'的雇員');
    when others then
      dbms_output.put_line('錯誤號:'||SQLCODE);
      dbms_output.put_line('錯誤號:'||sqlerrm);
  end;

  5.2raise_application_error
  用於在PL/SQL應用程序中自定義錯誤消息
  該過程只能在數據庫端的子程序(過程,函數,包,觸發器)中使用,不能在匿名快和客戶端的子程序中使用.
  raise_application_error(error_number,message[,[true|false]]);
  error_number:必須在-20000和-20999之間的負整數.MESSAGE不能超過2048字節
  TRUE:該錯誤會被放在先前錯誤堆棧中,FALSE:則會替換先前所有錯誤.
  create or replace procedure raise_comm(eno number,commission number) 
  is 
    v_comm emp.comm%type;
  begin
    select comm into v_comm from emp where empno=eno;
    if v_comm is null then 
       raise_application_error(-20001,'該雇員無補助');
    end if;
  exception
    when no_data_found then
      dbms_output.put_line('該雇員不存在');
  end;

  6.pl/sql編譯警告
  6.1PL/SQL警告分類:
  severe:該警告用於檢查可能出現的不可預料結果或錯誤結果,例如參數的別名問題
  performance:用於檢查可能出現的不可預料結果或錯誤結果,例如參數的別名問題
  inforamational:用於檢查子程序中的死代碼 
  all:該關鍵字用於檢查所有警告(severe,perforamce,informational)
 
  6.2控制PL/SQL警告消息
  為了使數據庫在編譯PL/SQL子程序時發出警告消息,需設置初始化參數PLSQL_WARNINGS.
  初始化PLSQL_WARNINGS不僅可在系統級或會話級設置,也可在ALTER PROCEDURE命令中進行設置激活或禁止
  alter system set plsql_warnings='enable:all';
  alter session set plsql_warnings='enable:performance';
  alter procedure hello compile plsql_warnings='enable:performance';
  alter session set plsql_warnings='disable:all';
  alter session set plsql_warnings='enable:severe','disable:performance','error:06002';
 
  當激活或禁止PL/SQL編譯警告時,不僅可以使用ALTER SYSTEM,ALTER SESSION,ALTER PROCEDURE命令,還可使用PL/SQL系統包DBMS_WARNINGS
  SQL>call dbms_warning.set_warning_setting_string('enable:all','session');
  
  7.使用PL/SQL編譯警告
  檢測死代碼
  為了檢測該子程序是否包含死代碼,必須首先激活警告檢查,然后重新編譯子程序,最后使用SHOW ERRORS命令顯示警告錯誤
  alter session set plsql_warnings='enable:informational';
  alter prodedure dead_code compile;
  show errors

  檢測引起性能問題的代碼
  編寫PL/SQL子程序時,如果數值與變量的數據類型不符合,ORACLE會隱含的轉換數據類型,但因為數據類型轉換會影響子程序性能,所以在編寫PL/SQL子程序時應該盡可能避免性能問題.
  create or replace procedure update_sal(name varchar2,salary varchar2)
  is
  begin
    upodate emp set sal=salary where ename=name;
  end;
  為了檢測該子程序是否會引起性能問題,應首先激活警告檢查,然后重新編譯子程序,最后再使用SHOW ERRORS顯示警告錯誤
  alter session set plsql_warnings='enable:performance';
  alter procedure update_sal compile;
  show errors

  
 

   
  14.使用LOB對象(large object)
  LOB類型專門用於存儲大對象的數據,包括大文本,圖形/圖象,視頻剪切等大數據
  1.LOB類型
  oracle將lob分為兩種:內部lob和外部lob
  內部lob包括:clob,blob和nclob三種類型,被存儲在數據庫中,並且支持事務操作
  外部LOB只有BFILE一種類型,該類型的數據被存儲在操作系統OS文件中,並且不支持事務操作.
  CLOB/NCLOB用於存儲用於存儲大批量字符數據,BLOB用於存儲大批量二進制數據,BFILE則存儲着指向OS文件的指針
  1.1.使用LOB列時,如果數據小於4000字節,則與其他列相鄰存放(行內),如果數據大於4000字節,則數據被存放到專門的LOB段中(行外)
  1.2.臨時LOB
  編寫PL/SQL應用程序時,可以使用臨時LOB.臨時LOB相當於局部變量,與數據庫表無關,並且只能又當前應用程序建立和使用
  當在SQL語句中使用臨時LOB時,他們只能作為輸入宿主變量使用,可在WHERE子句,VALUES子句和SET子句中使用臨時LOB,而不能在INTO子句中使用臨時LOB

  2.DBMS_LOG包
  2.1常量
  DBMS_LOB包定義了一些常量,這些常量可以在PL/SQL應用程序中直接引用.
  file_readonly constant binary_integer:=0;
  lob_readonly constant binary_integer:=0;
  lobmaxsize constant integer:=4294967295;
  call constant pls_integer:=12;
  session constant pls_integer:=10;
  2.2過程APPEND
  用於將源LOB變量的內容添加到目標LOB變量的尾部.
  該過程只適用與內部LOB類型(BLOB CLOB),不適合BFILE類型
  dbms_lob.append(dest_lob in out nocopy blob,src_lob in blob);
  dbms_lob.append(dest_lob in out nocopy character set any_cs,src_lob in clob character set dest_lob%charset);
  dest_lob用於指定目標LOB變量,SRC_LOB用於指定源LOB變量.
  declare
    dest_lob clob;
    src_lob clob;
  begin
    src_lob:='中國';
    dest_lob:='你好, ';
    dbms_lob.append(dest_lob,src_lob);
    dbms_output.put_line(dest_lob);
  end;

  3.過程CLOSE
  用於關閉已經打開的LOB,不僅使用於CLOB,NCLOB和BLOB類型,也使用於BFILE類型
  dbms_lob.close(lob_loc in out nocopy blob/clob/bfile);

  4.函數compare
  用於比較兩個LOB的全部內容或部分內容.不僅適用於CLOB,NCLOB,BLOB類型,也適用於BFILE類型
  只能用於比較同類型的LOB變量
  dbms_lob.compare(lob_1 in blob/clob/bfile,lob_2 in blob/clob/bfile,amount in integer:=4294967295,
                   offset_1 in integer:=1,offset_2 in integer:=1) return integer;
  lob_1指定第一個LOB變量,lob_2指定第二個lob變量,amount指定字符個數(CLOB)或字節個數(BLOB)
  offset_1指定第一個LOB的起始比較位置,offset_2指定第二個LOB的起始位置
  比較結果相同,則返回O,如果比較不同,則返回一個非0的整數
  declare
    dest_lob clob;
    src_lob clob;
  begin
    src_lob:='中國';
    dest_lob:='&content';
    if dbms_lob.compare(src_lob,dest_lob)=0 then
       dbms_output.put_line('內容相同');
    else
       dbms_output.put_line('內容不同');
    end if;
  end;
 
  5.過程copy
  用於將源LOB變量的部分或全部內容復制到目標LOB變量中,只適用於內部LOB類型(CLOB,NCLOB,BLOB),不適用BFILE類型
  dbms_lob.copy(
       dest_lob in out nocopy blob/clob/nclob,  
       src_lob in blob/clob/nclob,
       anount in integer,
       dest_offset in integer:=1,   指定要復制到目標LOB變量的起始位置
       src_offset in integer:=1);   指定源LOB變量中開始復制的起始位置
 

  declare
    dest_lob clob;
    src_lob clob;
    amount int;
  begin
    src_lob:='中國';
    dest_lob:='你好, ';
    amount:=dbms_lob.getlength(src_lob);
    dbms_lob.copy(dest_lob,src_lob,amout,3);
    dbms_output.put_line(dest_lob);
  end;

  
  6.過程CREATETEMPORARY
  用於建立臨時LOB,只適用於內部LOB類型(BLOB/CLOB/NCLOB),但不適用於BFILE類型.建立在用戶的臨時表空間
  dbms_lob.createtemporary(
      lob_loc in out nocopy blob/clob/nclob,
      cache in boolean,dur in pls_integer:=10);
  LOB_LOC指定LOB定位符,CACHE指定是否要將LOB讀取到緩沖區,DUR指定何時清除臨時LOB(10:會話結束清除臨時LOB,12:調用結束清除臨時LOB)
  declare
    src_lob clob;
  begin
    dbms_lob.createtemporary(src_lob,true);
  end;

  7.過程ERASE
  用於刪除LOB變量的全部內容或部分內容,只適用內部LOB類型(BLOB/CLOB/NCLOB),而不適用於BFILE類型
  DBMS_LOB.ERASE(
      lob_loc in out nocopy blob/clob/nclob,
      amount in out nocopy integer,
      offset in integer:=1);  --指定開始刪除內容的起始位置
  
  declare
    src_lob clob;
    offset int;
    amount int;
  begin
    src_lob:='歡迎使用PL/SQL編程指南';
    amount:=10;
    offset:=5;
    dbms_lob.erase)src_lob,amount,offset);
    dbms_output.put_line(src_lob);
  end;

  8.過程fileclose
  用於關閉已經打開的BFILE定位符所指向的OS文件
     dbms_lob.fileclose (file_loc in out nocopy bfile);
  file_loc用於指定BFILE定位符
  9.過程FILECOLSEALL
  用於關閉當前會話已經打開的所有BFILE文件
     dbms_lob.filecloseall
  10.函數FILEEXISTS
  用於確定BFILE定位符所指向的OS文件是否存在
      dbms_lob.fileexists(file_loc in bfile) return integer;
  如果文件存在,則返回1,如果文件不存在,則返回0
  declare
    file1 bfile;
  begin
    file1:=bfilename('G','readme.doc);
    if dbms_lob.fileexists(file1)=0 then
       dbms_output.put_line('文件不存在');
    else
       dbms_output.put_line('文件存在');
    end if;
  end;

  11.過程FILEGETNAME
  用於取得BFILE定位符所對應的目錄別名和文件名.
      dbms_lob.filegetname(file_loc in bfile,dir_alias out varchar2,filename out varchar2);
  dir_alias用於取得BFILE定位符所對應的目錄別名,FILENAME取得BFILE定位符所對應的文件名.
  declare
    dir_alias varchar2(20);
    filename varchar2(50);
    file_loc bfile;
  begin
    select filename into file_loc from bfile_table where fno=&no;
    dbms_lob.filegetname(file_loc,dir_alias,filename);
    dbms_output.put_line('目錄別名:'||dir_alias);
    dbms_output.put_line('文件名:'||filename);
  end;

  12函數fileeisopen
  用於確定BFILE所對應的OS文件是否已經打開
    dbms_lob.fileisopen(file_loc in bfile) return integer;
  如果已經被打開,則返回1,否則返回0
  declare
    file1 bfile;
  begin
    file1:=bfilename('G','readme1.doc');
    if dbms_lob.fileisopen(file1)=0 then
       dbms_output.put_line('文件未打開');
    else
       dbms_output.put_line('文件已經打開');
    end if;
  end;

  13.過程FILEOPEN
  用於打開BFILE所對應的OS文件
  dbms_lob.fileopen(
     file_loc in out nocopy bfile,
     open_mode in binary_integer:=file_readonly);
  open_mode用於指定文件的打開模式,OS文件只能以只讀方式打開.
  declare
    file1 bfile;
  begin'
    file1:=bfilename('G','readme.doc');
    if dbms_lob.fileexists(file1)=1 then
       dbms_lob.fileopen(file1);
       dbms_output.put_line('文件已經被打開');
    end if;
    dbms_lob.fileclose(file1);
  end;

  14.過程freetemporary
  用於釋放在默認臨時表空間中的臨時LOB
      dbms_lob.freetemporary(
         lob_loc in out nocopy blob/clob/nclob);
  LOB_LOC指定LOB定位符
  declare
    src_lob clob;
  begin
    dbms_lob.createtemporary(src_lob,true);
    src_lob:='中國';
    dbms_lob.freetemporary(src_lob);
  end;

  15函數GETCHUNKSIZE
  當建立包含CLOB列或BLOB列的表時,通過指定CHUNK參數可以指定操作LOB需要分配的字節數(數據塊的整數倍).
  通過使用函數GETCHUNKSIZE可以取得CHUNK參數所對應的值
  dbms_lob.getchunksize(lob_loc in blob/clob/nclob) return integer;
  
  declare
    src_lob clob;
    chunksize int;
  begin 
    src_lob:='中國';
    chunksize:=dbms_lob.getchunksize(src_lob);
    dbms_output.put_line('chunk 尺寸:'||chunksize);
  end;

  16.函數getlength
  用於取得LOB數據的實際長度.適用於CLOB和BLOB.BFILE類型
  dbms_lob.getlength(lob_loc blob/clob/nclob) return integer;
  declare
    file1 bfile;
    length int;
  begin
    file1:=bfilename('G','readme.doc');
    length:=dbms_lob.getlength(file1);
    dbms_output.put_line('文件長度:'||length);
  end;

  17.函數instr     
  用於返回特定樣式數據在LOB中從某偏移位置開始第N次出現時的具體位置,適合於BLOB,CLOB,BFILE類型
  dbms_lob.instr(lob_loc in blob/clob/nclob/bfile,pattern in raw/varchar2,
                 offset in integer:=1,nth in integer:=1) return integer;
  pattern指定要搜索的字符串或二進制數據,OFFSET指定搜索的起始位置,NTH指定第N次的出現次數
  declare
    src_lob clob;
    location int;
    offset int;
    occurence int;
  begin
    src_lob:='中國中國中國中國中國';
    offset:=2;
    occurence:=2;
    location:=dbms_lob.instr(src_lob,'中國',offset,occurence);
    dbms_output.put_line('從第'||offset||'字符開始,中國第'||occurence||'次出現的具體位置:'||location);
  end;

  18.函數isopen
  用於確定LOB是否已經被打開,適用於BLOB,CLOB,BFILE類型
  dbms_lob.isopen(lob_loc in blob/clob/bfile) return integer;
  已經打開返回1,否則返回0
  declare
    src_lob clob;
  begin
    src_lob:='中國,中國,偉大的中國');
    if dbms_lob.isopen(src_lob)=0 then
       dbms_lob.open(src_lob,1);
    end if;
    dbms_lob.close(src_lob);
  end;

  19.函數istemporary
  用於確定LOB定位符是否為臨時LOB
  dbms_lob.istemporary(lob_loc in blob/clob/nclob) return integer;
  是臨時LOB,則返回1,否則返回0
  declare
    src_lob clob;
  begin
    if dbms_lob.istemporary(src_lob)=1 then
       dbms_output.put_line('已經是臨時LOB');
    else
       dbms_output.put_line('臨時LOB需要建立');
       dbms_lob.createtemporary(src_lob,true);
    end if;
    dbms_lob.freetemporary(src_lob);
  end;

  20.過程LOADFROMFILE
  用於將BFILE的部分或者全部內容復制到目標LOB變量(CLOB,BLOB)中
  dbms_lob.loadfromfile(dest_lob in out nocopy blob/clob,
                        src_file in bfile,amount in integer,
                        dest_offset in integer:=1,src_offset in integer:=1);
  src_file指定BFILE定位符,數據裝載時不進行字符集轉換.
  declare
    src_lob bfile;
    dest_lob clob;
    amount int;
  begin
    src_lob:=bfilename('g','a.txt');
    dbms_lob.createtemporary(dest_lob,true);
    dbms_lob.fileopen(src_lob,0);
    amount:=dbms_lob.getlength(src_lob);
    dbms_lob.loadfromfile(dest_lob,src_lob,amount);
    dbms_lob.fileclose(src_lob);
    dbms_lob.freetemporary(dest_lob);
  end;

  21.過程LOADBLOBFROMFILE
  將BFILE數據裝載到BLOB中,並且在裝載后可以取得新的偏移位置
  dbms_lob.loadblobfromfile(dest_lob in out nocopy blob,src_bfile in bfile,
                            amount in integer,dest_offset in out integer,src_offset in out integer);
  src_bfile指定BFILE定位符,DEST_OFFSET(IN)指定目標LOB的起始位置,DEST_OFFSET(OUT)取得裝載后的偏移位置
  SRC_OFFSET(IN)指定BFILE定位符的起始位置,SRC_OFFSET(OUT)取得BFILE讀取完成后的偏移位置.
  declare
    src_lob bfile;
    dest_lob blob;
    amount int;
    src_offset int:=1;
    dest_offset int:=1;
  begin
    src_lob:=bfilename('G','a.txt');
    dbms_lob.createtemporary(dest_lob,true);
    dbms_lob.fileopen(src_lob,0);
    amount:=dbms_lob.getlength(src_lob);
    dbms_lob.loadblobfromfile(dest_lob,src_lob,amount,dest_offset,src_offset);
    dbms_lob.fileclose(src_lob);
    dbms_lob.freetemporary(dest_lob);
    dbms_output.put_line('新的偏移位置:'||dest_offset);
  end;

  22.過程loadclobfromfile
  將BFILE數據裝載到CLOB中.當使用該過程裝載到CLOB中,可以指定字符集ID號,並進行字符集轉換.
  dbms_lob.loadclobfromfile(
                            dest_lob in out nocopy clob,
                            src_bfile in bfile,amout in integer,
                            dest_offset in out integer,
                            src_offset in out integer,
                            src_csid in number,                   --指定源文件的字符集ID號
                            lang_context in out integer,          --指定語言上下文(IN)取得先前裝載語言上下文(OUT)
                            warning out integer);
  declare
    src_lob bfile;
    dest_lob clob;
    amount int;
    src_offset int:=1;
    csid int:=0;
    lc int:=0;
    warning int;
  begin
    src_lob:=bfilename('G','a.txt');
    dbms_lob.createtemporary(dest_lob,true);
    dbms_lob.fileopen(src_lob,0);
    amount:=dbms_lob.getlength(src_lob);
    dbms_lob.loadclobfromfile(dest_lob,src_lob,amount,dest_offset,src_offset,csid,lc,warning);
    dbms_lob.fileclose(src_lob);
    dbms_output.put_line(dest_lob);
    dbms_lob.freetemporary(dest_lob);
  end;

  23.過程OPEN
  在打開LOB時指定LOB的讀寫模式.只讀(DBMS_LOB.LOB_READONLY)  讀寫(DBMS_LOB.LOB_READWRITE)
  使用於BLOB,CLOB,BFILE
  dbms_lob.open(lob_loc in out nocopy blob/clob/bfile,open_mode in binary_integer);
  
  declare
    src_lob clob;
    v1 varchar2(100):='中國';
    amount int;
  begin
    amount:=length(v1);
    dbms_lob.createtemporary(src_lob,true);
    dbms_lob.open(src_lob,dbms_lob.lob_readwrite);
    dbms_lob.write(src_lob,amount,1,v1);
    dbms_lob.colse(src_lob);
    dbms_output.put_line(src_lob);
    dbms_lob.freetemporary(src_lob);
  end;

  24.過程READ
  用於將LOB數據讀取到緩沖區,適用於BLOB,CLOB,BFILE
  DBMS_LOB.READ(LOB_LOC IN BLOB/CLOB/BFILE,AMOUT IN OUT NOCOPY BINARY_INTEGER,
                OFFSET IN INTEGER,BUFFER OUT RAW/VARCHAR2);
  declare
    src_lob clob:='偉大的中國';
    amount int;
    buffer varchar2(200);
    offset int:=1;
  begin
    amount:=dbms_lob.getlength(src_lob);
    dbms_lob.open(src_lob,dbms_lob.lob_readonly);
    dbms_lob.read(src_lob,amount,offset,buffer);
    dbms_output.put_line(buffer);
    dbms_lob.close(src_lob);
  end;
  
  25.函數SUBSTR
  用於返回LOB中從指定位置開始的部分內容,適用BLOB,CLOB,BFILE
  dbms_lob.substr(lob_loc in blob/clob/bfile,aount in integer:=32767,offet in integer:=1) return raw;
  declare
    src_lob clob:='中國,中國,偉大的中國';
    amount int;
    v1 varchar2(200);
    offset int;
  begin
    amount:=10;
    offset:=4;
    v1:=dbms_lob.substr(src_lob,amout,offset);
    dbms_output.put_line(v1);
  end;

  26.過程trim
  用於截斷LOB內容到指定長度,只適用BLOB和CLOB,不適用於BFILE
  dbms_lob.trim(lob_loc in out nocopy blob/clob/nclob,newlen in integer);
  newlen用於指定截斷后的LOB長度
  declare
    src_lob clob:='中國,中國,偉大的中國';
    amount int;
  begin
    amout:=5;
    dbms_lob.trim(src_lob,amout);
    dbms_output.put_line(src_lob);
  end;

  27過程write
  用於將緩沖區數據寫入到LOB中的特定位置,只適用BLOB,CLOB,不適用BFILE
  dbms_lob.write(lob_loc in out nocopy blob/clob,amout in binary_integer,
                 offset in integer,buffer in raw/varchar2);
  buffer用於指定要寫入的內容
  declare
    src_lob clob:='我的祖國';
    amount int;
    offset int;
    buffer varchar2(100):=',偉大的中國':
  begin
    offset:=dbms_lob.getlength(src_lob)+1
    amount:=length(buffer);
    dbms_lob.write(src_lob,amout,offset,buffer);
    dbms_output.put_line(src_lob);
  end;

  28.過程WRITEAPPEND
  用於將緩沖區數據寫人到LOB尾部,只適用於BLOB,CLOB,不適用BFILE
  DBMS_LOB.WRITEAPPEND(LOB_LOC IN OUT NOCOPY BLOB/CLOB/NCLOB,AMOUT IN BINARY_INTEGER,BUFFER IN RAW);
 
  declare
    src_lob clob:='我的祖國';
    amount int;
    buffer varchar2(100):=',偉大的中國';
  begin
    amount:=length(buffer);
    dbms_lob.writepaaend(src_lob,amount,buffer);
    dbms_output.put_line(src_lob);
  end;

 


  14.3訪問LOB
  1.建立包含CLOB列的表
  create table lob_example1(id number(6) primary key,name varchar2(10),resume clob);

  2.初始化CLOB列
  使用函數EMPTY_CLOB()初始化CLOB列,分配了LOB定位符
  insert into lob_example1 values(1,'王名',empty_clob());
  insert into lob_example1 values(2,'馬麗',empty_clob());
  commit;

  3.更新CLOB列的數據
  write  writeappend
  如果要更新CLOB列的數據,那么在檢索CLOB列時就必須帶有FOR UPDATE子句
  declare
    lob_loc clob;
    text varchar2(200);
    amount int;
    offset int;
  begin
    select resume into lob_loc from lob_example1 where id=&id for update;
    offset:=dbms_lob.getlength(lob_loc)+1;
    text:='&resume';
    amount:=length(text);
    dbms_lob.write(lob_loc,amount,offset,text);
    commit;
  end;

  4.讀取CLOB列的數據
  為了讀取CLOB列的所有數據,應該使用循環方式進行處理(READ)
  declare
    lob_loc clob;
    buffer varchar2(200);
    amount int;
    offset int;
  begin
    select resume into lob_loc from lob_example1 where id=&id;
    offset:=6;
    amount:=dbms_lob.getlength(lob_loc);
    dbms_lob.read(lob_loc,amount,offset,buffer);
    dbms_output.put_line(buffer);
  end;

  5.將文本內容寫入到CLOB列
  為了避免字符集問題,建議使用LOADCLOBFROMFILE
  declare
    lobloc clob;
    fileloc bfile;
    amount int;
    src_offset int:=1;
    dest_offset int:=1;
    csid int:=0;
    lc int:=0;
    warning int;
  begin
    fileloc:=bfilename('G','馬麗.txt');
    dbms_lob.fileopen(fileloc,0);
    amount:=dbms_lob.getlength(fileloc);
    select resume into lobloc from lob_example1 where id=2 for update;
    dbms_lob.loadclobfromfile(lobloc,fileloc,amount,dest_offset,src_offset,csid,lc,warning);
    dbms_lob.fileclose(fileloc);
    commit;
  end;

  
  6.將clob列內容寫入到文本文件
  要將CLOB列的內容寫入到文本文件時,不僅需要使用DBMS_LOB包讀取CLOB列的內容,而且需要使用UTL_FILE包建立文本文件並寫入內容
  declare
    lobloc clob;
    amount int;
    offset int:=1;
    buffer varchar2(2000);
    handle UTL_FILE.FILE_TYPE;
  begin
    select resume into lobloc from lob_example1 where id=&id;
    amount:=dbms_lob.getlength(lobloc);
    dbms_lob.read(lobloc,amount,offset,buffer);
    handle:=utl_file.fopen('user_dir','a.txt','w',2000);
    utl_file.put_line(handle,buffer);
    utl_file.fclose(handle);
  
  14.4.訪問BLOB(二進制數據)
  1.建立包含BLOB列的表
  create table lob_example2(id number(6) primary key,name varchar2(10),photo blob);
 
  2.初始化BLOB列
  使用函數EMPTY_BLOB()初始化BLOB列
  insert into lob_example2 values(1,'王名',empty_blob());
  insert into lob_example2 values(2,'馬麗',empty_blob());
  commit;

  3.將二進制文件內容寫入到BLOB列
  loadblobfromfile
  declare
    lobloc blob;
    fileloc bfile;
    amount int;
    src_offset int:=1;
    dest_offset int:=1;
  begin
    select photo into lobloc from lob_example2 where id=&id for update;
    fileloc:=bfilename('G','&filename');
    dbms_lob.fileopen(fileloc,0);
    amount:=dbms_lob.getlength(fileloc);
    dbms_lob.loadblobfromfile(lobloc,fileloc,amount,dest_offset,src_offset);
    dbms_lob.fileclose(fileloc);
    commit;
  end;
 
  4.讀取BLOB列數據
  因為BLOB列中存放着二進制數據,當讀取數據時應該使用RAW變量接受其數據READ
  declare
    lobloc blob;
    buffer raw(2000);
    amount int;
    offset int:=1;
  begin
    select photo into lobloc from lob_example2 where id=&id;
    amount:=dbms_lob.getlength(lobloc);
    dbms_lob.read(lobloc,amount,offset,buffer);
  end;
  讀取到BUFFER中,如果大小大於2000字節,需要使用循環方式讀取數據.

  5.將BLOB列的內容寫入到二進制文件
  不僅需要使用DBMS_LOB包讀取BLOB列的內容,而且需要使用UTL_FILE包建立二進制文件並寫入內容
  declare
    lobloc blob;
    amount int;
    offset int:=1;
    buffer raw(1000);
    handle utl_file.file_type;
  begin
    select photo into lobloc from lob_example2 where id=&id;
    amount:=dbms_lob.getlength(lobloc);
    dbms_lob.read(lobloc,amount,offset,buffer);
    handle:=utl_file.fopen('user_dir','a.bmp','w',1000);
    utl_file.put_raw(handle,buffer);  ---寫入到文本文件
    utl_file.fclose(handle);
  end;

  14.5訪問BFILE
  BFILE存放着指向OS文件的指針,所對應的OS文件內容只能讀取,不能修改.
  當ORACLE數據庫中使用BFILE類型訪問OS文件時,必須首先建立DIRECTORY對象,而建立DIRECTORY對象則要求用戶必須具有CREATE ANY DIRECTORY權限.
  conn system/manager
  grant create any directory to scott;
  conn scott/tiger
  create directory bfile_dir as 'G:\BFILE_EXAMPLE';
  當建立DIRECTTORY對象時,一定要確保OS目錄已經存在.  1.建立包含BFILE列的表
  create table lob_example3(
    id number(6) primary key,name varchar2(10),resume bfile);
  2.初始化BFILE列
  使用函數BFILENAME()來初始化BFILE列
  當使用BFILENAME()函數時,DIRECTORY對象必須使用大寫格式
  insert into lob_example3 values(1,'王名',bfilename('BFILE_DIR','王名.TXT'));
  insert into lob_example3 values(2,'馬麗',bfilename('BFILE_DIR','王麗.TXT'));
  3.讀取BFILE列的內容
  使用DBMS_LOB包的READ過程,應該使用RAW變量接受其讀出的數據
  declare
    buffer raw(2000);
    amount int;
    offset int;
    lobloc bfile;
  begin
    select resume into lobloc from lob_example3 where id=&id;
    dbms_lob.fileopen(lobloc,0);
    amount:=dbms_lob.getlength(lobloc);
    offset:=1;
    dbms_lob.read(lobloc,amount,offset,buffer);
    dbms_lob.fileclose(lobloc);
  end;
 

原文:http://www.cnblogs.com/BradMiller/archive/2010/06/06/1752852.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM