欢迎来到我的地盘:今天是

若得山花插满头,莫问奴归处!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
课程一、声明变量
   本课重点:
   1、了解基本的PLSQL块和区域
   2、描述变量在PLSQL中的重要性
   3、区别PLSQL与非PLSQL变量
   4、声明变量
   5、执行PLSQL块
   一.概述
 1、PLSQL 块结构:
       DECLARE --- 可选
        变量声明定义
       BEGIN  ---- 必选
        SQL 和PLSQL 语句
       EXCEPTION ---- 可选
       错误处理
       END;---- 必选
   二、实例:
       declare
       vjob varchar(9);
       v_count number:=0;
       vtotal date:=sysdate+7;
       c_tax constant number(3,2):=8.25;
       v_valid boolean not null:=true;
      begin
       select sysdate into vtotal from dual;
         end;
           /
      上例中,如果没有这个SELECT语句,会如何?
       出错,说明必须有STATEMENTS
       如果: select sysdate from dual into vtotal ;
       同样,也不行。而且变量与赋值的类型要匹配。
   三、%TYPE的属性
       声明一个变量使之与数据库某个列的定义相同或与另一个已经定义过的变量相同
       所以%TYPE要作为列名的后缀:如:
       v_last_name s_emp.last_name%TYPE;
       v_first_name s_emp.first_name%TYPE; --这样做的好处是我们不必去知晓此列的类型与定义
       或:v_balance NUMBER(7,2);
       v_minimum_balance v_balance%TYPE := 10;
   四、声明一个布尔类型的变量
       1 只有TRUE、FALSE、NULL可以赋值给BOOLEAN变量
       2 此变量可以接逻辑运算符NOT、AND、OR。
       3 变量只能产生TRUE、FALSE、NULL。
       实例:
       VSAL1:=50000;
       VSQL2:=60000;
       VCOMMSAL BOOLEAN:=(VSAL1<VSQL2);
       --其实是把TRUE赋值给此变量。
   五、BLOB 类型的变量
     共有CLOB、BLOB、BFILE、NCLOB几种,这里不做为重点。
   六:使用HOST VARIABLES
     SQL> variable n number
      SQL> print n
     :n = v_sal /12;
     :n这个加了:前缀的变量不是PLSQL变量,而是HOST。
    七、以下几个PLSQL声明变量,哪个不合法?
     A 、DECLARE
          V_ID NUMBER(4);
     B、DECLARE
          V_X,V_Y,V_Z VARCHAR2(9);
     C、DECLARE
         V_BIRTH DATE NOT NULL;
     D、DECLARE
         V_IN_STOCK  BOOLEAN:=1;
     E、DECLARE
         TYPE NAME_TAB IS TABLE OF VARCHAR2(20)
            INDEX BY BINARY_INTEGER;
         DEPT_NAME NAME_TAB;
课程二、写执行语句
 本课重点:
   1、了解PLSQL执行区间的重要性
   2、写执行语句
   3、描述嵌套块的规则
   4、执行且测试PLSQL块
   5、使用代码惯例
   一、PLSQL 块的语法规则:
     1、语句可以跨跃几行。
     2、词汇单元可以包括:分隔符、标识符、文字、和注释内容。
     3、分隔符:
      +-*/=<>||....
     4、标识符:
      最多30个字符,不能有保留字除非用双引号引起。
      字母开头,不与列同名。
     5、文字串:如 V_ENAME:='FANCY';要用单引号括起来。
        数值型可以用简单记数和科学记数法。
     6、注释内容:单行时用--    多行用/*   */
        与C很相似
   二、SQL函数在PL/SQL的使用:
     1、可用的:
      单行数值型、字符型和转换型,日期型。
     2、不可用的:
      最大、最小、DECODE、分组函数。
      实例:
      BEGIN
         SELECT TO_CHAR(HIREDATE,'MON,DD,YYYY') FROM EMP;
      END;
      V_comment:=user||':'||sysdate; -- 会编译出错
      V_comment:=user||':'||to_char(sysdate); --正确
      如果有可能,PLSQL都会进行数据一致性的转换,但ORACLE推荐你应该进行显示的转换,因为这样会提高性能。
   三、嵌套块和变量作用区域
     1、执行语句允许嵌套时嵌套。
     2、嵌套块可以看作正常的语句块。
     3、错误处理模块可以包括一个嵌套块
     4、exponential指数 逻辑、算数、连接、小括号
     5、看正面实例:
  declare
  vjob varchar(9);
  v_count number:=0;
  vtotal date:=sysdate +7;
  c_tax constant number(3,2):=8.25;
  v_valid boolean not null:=true;
  ttt vtotal%type;
  begin
  --select sysdate into vtotal from dual;--体会有无此句与结果的影响
  dbms_output.put_line (vtotal);
  end;
  /
   注意:在执行块之前,要在SQL PLUS中执行:SET SERVEROUTPUT ON
   四、以实例来说明函数的参数声明作用域
    declare
     v_weight number(3):=600;
  v_message varchar2(255):='product10000';
  begin
   declare
      --sub-block
   v_weight number(3):=1;
   v_message varchar2(255):='pro300';
   begin
    v_weight:=v_weight +1;
  end;
  v_weight:=v_weight +1;
  v_message:=v_message || 'my name';
  end;
  /
    子块中的V_WEIGHT值为 2
     我们可以在子块中加入:dbms_output.put_line('subblock value is '||v_weight);
     在主体中加入:dbms_output.put_line('main value is '||v_weight);
     我们发现MAINBLOCK中V_WEIGHT为 601
     改动:
     1、在主块的声明中加  v_date date default sysdate;
     在子块中加入:dbms_output.put_line('subblock date value is '||v_date);
     执行结果:subblock date value is 22-11月-01
     ****说明:主块中的变量,如果子块中没有同名变量声明,则继承主块中的声明和初始化值;
     2、在子块中加入:v_sub char(9);
       dbms_output.put_line('subblock char value is '||v_sub);
       此时正常输出。
       在主块中加入:dbms_output.put_line('main char value is '||v_sub);
       输出:ORA-06550: 第 21 行, 第 45 列:
       PLS-00201: 必须说明标识符 'V_SUB'
       说明:
       子块中声明的变量主块中并不知晓,因此出错。
       了解了此实例,一切情况的变量的值的走向就都明了了。
课程三、与ORACLE SERVER交互
 一、PLSQL中的SQL语句:
     SELECT、DML、COMMIT、ROLLBACK、SAVEPOINT、CURSOR
     特殊强调:PLSQL不支持DCL,不要问为什么。(DBMS_SQL package allows you to issue DDL and DCL statements.)
  二、SELECT
     SELECT select_list
  INTO variable_name | record_name
  FROM table
  WHERE condition;
     例:
  SQL> r
   1  declare
   2  v_deptno number(2);
   3  v_loc varchar2(15);
   4  begin
   5     select deptno,loc
   6       into v_deptno,v_loc
   7      from dept
   8      where dname='SALES';
   9  DBMS_OUTPUT.PUT_LINE (V_deptno ||' and '||v_loc);
   10 end;
     选取字段与变量个数和类型要一致。声明的变量一定要在SIZE上大于返回的赋值,否则提示缓冲区溢出。
     如果SELECT语句没有返回值:ORA-01403: 未找到数据
                               ORA-06512: 在line 5
     如果有多个值返回:ORA-01422: 实际返回的行数超出请求的行数
     这些我们到了错误处理时会逐一讲解。
     上面的例子可以改为:
     declare
     v_deptno dept.deptno%type;
     v_loc dept.loc%type;
     begin
      select deptno,loc
        into v_deptno,v_loc
        from dept
       where dname='SALES';
     DBMS_OUTPUT.PUT_LINE (V_deptno ||' and '||v_loc);
   end;
  /
  这样,可以在未知其他字段大小和类型的时候定义变量,提高兼容性。
 三、DML 操作:
     1、实例:
     declare
   v_empno emp.empno%type;
   begin
      select max(empno)
       into v_empno
       from emp;
      v_empno:=v_empno+1;
      insert into emp(empno,ename,job,deptno)
        values(v_empno,'asdfasdf','ddddd',10);
   end;
  /
     这样也可以实现如SEQUENCE一样的编号唯一递增。
     2、更新和删除:
      这个较为简单:
      DECLARE
         V_DEPTNO EMP.DEPtno%type :=10;
     begin
        delete from emp
        where deptno=v_deptno;
     end;
  /
  PLSQL首先检查一个标识符是否是一个数据库的列名,如果不是,再假定它是一个PLSQL的标识符。所以如果一个PLSQL的变量名为ID,列中也有个ID,如:
  SELECT date_ordered, date_shipped
  INTO date_ordered, date_shipped
  FROM s_ord
  WHERE id = id;
     就会返回TOO MANY ROWS,这是要尽量避免的。   
 四、SQL CURSOR
     游标是一个独立SQL工作区,有两种性质的游标:
     隐式游标:  当PARSE 和EXECUTE 时使用隐式游标。
     显式游标:  是由程序员显式声明的。
     游标的属性:
     SQL%ROWCOUNT:一个整数值,最近SQL语句影响的行数。
     SQL%FOUND    BOOLEAN属性,如果为TRUE,说明最近的SQL STATEMENT有返回值。
     SQL%NOTFOUND  与SQL%FOUND相反
     SQL%ISOPEN   在隐式游标中经常是FALSE,因为执行后立即自动关闭了。
     SQL> variable row_de number
     SQL> r
      1  declare
      2    v_deptno number:=10;
       3    begin
      4      delete from emp where
      5       deptno=v_deptno;
      6       :row_de:=sql%rowcount;
      7 end;
  PL/SQL 过程已成功完成。
  SQL>  print row_de  --这是一个SQL PLUS变量
      ROW_DE
   ----------
          4
     这时其实并没有真正的删除,而是需要 COMMIT或ROLLBACK,来完成事务。
课程四、 编写控制结构语句
 一、控制执行流
  例子:IF V_ENAME='OSBORNE' THEN
                  V_MGR:=22;
              END IF;
        这里我们可以注意,PLSQL和C语言或JAVA在条件上的不同,=代表关系运算,而:=代表赋值。
  看一个函数:
      create    FUNCTION calc_val
   (v_start IN NUMBER)
   RETURN NUMBER
   IS
   BEGIN
    IF v_start > 100 THEN
     RETURN (2 * v_start);
    ELSIF v_start >= 50 THEN
     RETURN (.5 * v_start);
    ELSE
     RETURN (.1 * v_start);
    END IF;
   END calc_val;
 二、注意LOGIC TABLE中的逻辑对应关系
  1、NOT、AND、OR
     2、任何表达式中含有空值结果都为 NULL
     3、连接字符串中含有空值会把NULL作为 EMPTY STRING
     declare
     v_deptno dept.deptno%type;
     v_loc dept.loc%type;
  V_FLAG BOOLEAN ;                                               
        V_REC BOOLEAN :=FALSE; --此值改为TRUE、NULL、FALSE进行不同的比较
        V_AVA BOOLEAN:=NULL;                                           
            begin                                                      
        V_FLAG:=V_REC AND V_AVA;                                       
        IF V_FLAG=TRUE THEN                                            
           DBMS_OUTPUT.PUT_LINE ('TRUE');                              
        ELSIF V_FLAG=FALSE THEN                                        
           DBMS_OUTPUT.PUT_LINE ('FALSE');                             
        ELSE                                                           
          DBMS_OUTPUT.PUT_LINE ('NULL');                               
        END IF;                                                        
         end;                                                          
        /                                                              
        值得注意的是:NULL AND FALSE ---> FALSE
   三、基本循环基础:
 1、LOOP
 statement1;
 statement2;
 . . .
 EXIT [WHEN condition];
 END LOOP;
    例如:
    v_ord_id s_item.ord_id%TYPE := 101;
    v_counter NUMBER (2) := 1;         
    BEGIN                              
    . . .                              
    LOOP                               
    INSERT INTO s_item (ord_id, item_id)
    VALUES (v_ord_id, v_counter);      
    v_counter := v_counter + 1;        
    EXIT WHEN v_counter > 10;          
    END LOOP;   
    2、FOR循环:
 FOR index IN [REVERSE] lower_bound..upper_bound LOOP
 statement1;
 statement2;
 . . .
 END LOOP;
 实例:DECLARE
    V_LOWER NUMBER:=1;
    V_UPPER NUMBER:=23;
 BEGIN
  DBMS_OUTPUT.PUT_LINE('');
     FOR I IN V_LOWER..V_UPPER LOOP
   DBMS_OUTPUT.PUT_LINE(I);
  END LOOP;
 END;
 3、WHILE 循环:
 WHILE condition LOOP
 statement1;
 statement2;
 . . .
 END LOOP;
 4、循环是可以多层嵌套的。可以用<<LABEL>>做循环的标签。
 BEGIN                                    
    <<Outer–loop>>LOOP                      
    v_counter :=v_counter+1;                                    
    EXIT WHEN v_counter>10;                              
    <<Inner–loop>>LOOP                      
    ...                                      
    EXIT Outer_loop WHEN total_done = ’YES’;
    –– Leave both loops                    
    EXIT WHEN inner_done = ’YES’;          
    –– Leave inner loop only               
    ...                                      
    END LOOP Inner_Loop;                     
    ...                                      
    END LOOP Outer_loop;                     
    END;      
课程五、使用组合数据类型* 游标操纵数据
 本课重点:
   1、创建用户自定义的PLSQL记录
   2、利用%ROWTYPE属性来创建记录
   3、创建PLSQL表
   4、描述记录、表、记录的表之间的区别
 一、合成数据类型
      1、类型分为PLSQL记录和PLSQL表
      2、包含内部组件
      3、可重用
 二、PLSQL记录
     与3GL中的记录结构相似
     与数据库表是两回事
     是一个方便的途径FETCH一些行FROM一个表来进行相关处理。
 declare                                                                                       
    vjob varchar(9);                                                
    v_count number:=0;                                              
    vtotal date:=sysdate +7;                                        
    c_tax constant number(3,2):=8.25;                               
    v_valid boolean not null:=true;                                 
    ttt vtotal%type;                                                
    type emp_record_type is record                                  
      (empno number not null:=100,                                  
        ename emp.ename%type,                                       
         job  emp.job%type);                                        
    emp_record emp_record_type;                                     
    begin                                                           
    --select sysdate into vtotal from dual;--体会有无此句与结果的影响
    dbms_output.put_line (vtotal);                                  
    end;            
    主要看TYPE RECORD出现的位置。每一个例子都是可以成功执行的。
    我们也可以利用原有的表结构:
    DECLARE
       EMP_RECORD EMP%ROWTYPE;
三、游标操纵数据
    PLSQL游标提供了一种从数据库提取多行数据,然后对每行数据进行单独处理的方法。
    1.两种游标:显式游标、隐式游标
    2.显式游标:操纵步骤如下:声明游标、打开游标、从游标中取回数据、关闭游标
   声明游标:                                                                                                                                    
            DECLARE CURSOR_NAME                                                                                                                                                           
             IS                                                                                                                                       
            SELECT STATMENT                                                                                                                           
               能够控制游标的,唯一参数是INIT.ORA中的OPEN_CURSORS,我原来以为是客户端最多可以打开多少个游标,但有本书上讲这是用于管理游标的内存的数量。
            DECLARE                                                                                                                                   
              CURSOR C_NAME                                                                                                                           
             IS                                                                                                                                       
             SELECT ENAME FROM EMP                                                                                                                    
             WHERE DEPTNO IN (SELECT DEPTNO FROM DEPT                                                                                                
             WHERE CITY_ID=‘BJ’)                                                                                                               
           --- 说明游标可以用子查询
      打开游标:
        OPEN CURSOR_NAME;
   这时游标将它的指针指向活动集的开始,指针指向第一条记录的前面是因为它还没有执行FETCH命令。如果试图打开一个已经打开的游标,将出错:
      ORA-06511:PL/SQL:CURSOR ALREADY OPEN
      我们可以这样:
            IF NOT C_NAME%ISOPEN
            THEN                
            OPEN C_NAME;       
            END IF;
      从游标中取回数据:
        FETCH CURSOR_NAME INTO RECOR-LIST;
      关闭游标:CLOSE CURSOR_NAME
  DECLARE                      
            myname varchar2(22);     
            CURSOR C_NAME                                                                                                                      
             IS                      
             SELECT ENAME FROM EMP;  
        begin                        
        IF NOT C_NAME%ISOPEN         
            THEN                     
            OPEN C_NAME;             
        end if;                      
        LOOP                         
        FETCH c_name into myname;    
        dbms_output.put_line (myname);
        exit when c_name%notfound;   
        end loop;                    
        close c_name;                
        end;
        /--我们将对以上程序进行变形,形成复杂的光标利用。                                                              
        DECLARE                                                                             
            myname varchar2(22);                                                            
            thisdeptno scott.emp.deptno%type;                                               
              CURSOR C_NAME                                                                 
             IS                                                                             
             SELECT ENAME,deptno FROM EMP order by deptno desc;                             
        begin                                                                               
        IF NOT C_NAME%ISOPEN                                                                
            THEN                                                                            
            OPEN C_NAME;                                                                    
        end if;                                                                             
        LOOP                                                                                
        FETCH c_name into myname,thisdeptno;                                                
        dbms_output.put_line (myname||','||thisdeptno || ',' || to_char(c_name%rowcount));  
        exit when c_name%notfound;                                                          
        end loop;                                                                           
        dbms_output.put_line ('the Total record is fetched is ' || to_char(c_name%rowcount));
        close c_name;                                                                       
        end;                                                                                
        /                
        我们增加变量,进行用了排序,使用了光标属性,大家看结果发生的变化,想想为什么。
  实例精华!!!:   DECLARE                                                                                        
            myname varchar2(22);                   
            ii number;                             
            thisdeptno scott.emp.deptno%type;      
              CURSOR C_NAME                        
             IS                                    
             SELECT * FROM EMP order by deptno desc;
             emp_record c_name%rowtype;            
        begin                                      
        ii:=1;                                     
            for emp_record in c_name loop          
         dbms_output.put_line(ii);                 
        ii:=ii+1;                                  
        end loop;                                  
        end;                                       
        / 
        --这里使用了游标FOR循环,在FOR循环的开始,进行、和END LOOP,分别隐式进行了游标的打开、FETCH和CLOSE。
     我们甚至可以不声明游标:FOR emp_record in (SELECT * FROM DEPT) loop
     这种技术被称为显式游标的自动化。
     在上面,我们可以将一个表的所有字段输出,如我们将PUT_LINE的II改为emp_record.ename,就可以输出一个字段内容。
      这种方式非常简单而且效率较高。
      为了测试光标属性的重要性,我们做一个以下的过程:                                 
         create or replace PROCEDURE change_salary                                                                        
         (v_emp_id IN NUMBER, -- formal parameters                                       
         v_new_salary IN NUMBER)                                                         
         IS                                                                              
         BEGIN -- begin PL/SQL block                                                     
         UPDATE emp                                                                      
         SET sal = v_new_salary                                                          
         WHERE empno = v_emp_id;                                                         
         COMMIT;                                                                         
         END change_salary;                                                              
        /                                                                                
              这样,我们在匿名块中,                                                     
        UPDATE DEPT                                                                      
            SET DNAME='MY DEPT' WHERE ....;                                              
          IF SQL%FOUND THEN                                                              
             COMMIT;                                                                     
          ELSE                                                                           
          change_salary(7369,9000);                                                      
        END IF;                                                                          
          我们看到我们通过流程控制了不同的执行结果,对于过程,我们可以用以下几种方法调用:
        在SQLPLUS中:CALL change_salary(7369,9000);                                      
                     EXECUTE change_salary(7369,9000);                                   
        在一个块中,如:                                                                 
            begin                                                                        
        change_salary(7369,9000);                                                        
        end;                                                                             
        /            
第六课、异常处理
      1、定义PLSQL异常                                                                                                 
         2、列举不同的异常处理方法                    
         3、捕获非预期的错误                          
         4、描述异常的影响                            
         5、定制异常的返回信息    一、PLSQL异常处理   
         异常是由ORACLE错误或显式的抛出一个错误产生的。
         如何处理:                                   
         用一个处理程序来捕获它;                     
         将它传递给CALLING ENVIRONMENT       
    二、异常的类型:
     ORACLE SERVER 预定义错误
     非ORACLE SERVER 预定义错误,但也是ORACLE SERVER 的标准错误
     用户自定义异常
    三、捕捉异常的要点:
    Place the WHEN OTHERS clause after all other exception handling clauses.
    You can have at most one WHEN OTHERS clause.
    Begin exception-handling section of the block with the keyword EXCEPTION.
    Define several exception handlers, each with their own set of actions, for the block.
    When an exception occurs, PL/SQL will process only one handler before leaving the block.
   
    EXCEPTION
 WHEN exception1 [OR exception2 . . .] THEN
 statement1;
 四、常用错误:                
         NO_DATA_FOUND ORA-01403       
         TOO_MANY_ROWS ORA-01422  
         INVALID_CURSOR ORA-01001 
         ZERO_DIVIDE ORA-01476    
         DUP_VAL_ON_INDEX ORA-00001
    五、实例                                           
        PROCEDURE elim_inventory                       
    (v_product_id IN s_product.id%TYPE) IS             
    v_id s_product.id%TYPE;                            
    BEGIN                                              
    SELECT id                                          
    INTO v_id                                          
    FROM s_product                                     
    WHERE id = v_product_id;                           
    DELETE FROM s_inventory                            
    WHERE product_id = v_product_id;                   
    COMMIT;                                            
    EXCEPTION                                          
    WHEN NO_DATA_FOUND THEN                            
    ROLLBACK;                                          
    TEXT_IO.PUT_LINE(TO_CHAR(v_product_id)||           
    ’ is invalid.’);                                 
    WHEN TOO_MANY_ROWS THEN                            
    ROLLBACK;                                          
    TEXT_IO.PUT_LINE(’Data corruption in S_PRODUCT.’);
    WHEN OTHERS THEN                                   
    ROLLBACK;                                          
    TEXT_IO.PUT_LINE(’Other error occurred.’);       
    END elim_inventory;              
    六、使用non-predefined Oracle7 Server error                                                     
        DECLARE                                                                   
           E_PRO   EXCEPTION;                                                     
           PRAGMA EXCEPTION_INIT(E_PRO,ERROR_NUMBER);                             
        BEGIN                                                                     
       ......                                                                     
         EXCEPTION                                                                
          WHEN E_PRO THEN                                                         
           DBMS_OUTPUT.PUT_LINE('ASDLKFJKASDJFASJDFLKASDF');                      
          ......                                                                  
        END;                                                                      
    七、用户自定义                                                            
        exception EXCEPTION;                                                      
       RAISE exception;    EXCEPTION                                              
          WHEN E_PRO THEN                                                         
           DBMS_OUTPUT.PUT_LINE('ASDLKFJKASDJFASJDFLKASDF');                      
          ......                                                                  
        END;     这里,只有用户自定义异常是要显式声明的,其他两个不用。           
        在SUN OS5.8中,进行SVRMGRL> OERR ORA 01840  可返回信息                    
        或查错误代码:                                                            
        HTTP://TECHNET.ORACLE.COM/DOC/SERVER.815/A67785/E1500.HTM                 
            题外话,create public database link otlink connect to system identified
        by manager using 'oratest';                                               
            说回来,以下两个函数:                                                
       SQLCODE ----Returns the numeric value for the error code. You can assign it
    to a NUMBER variable.                                                         
       SQLERRM ----Returns character data containing the message associated with  
    the error number.                                                             
        一般这样,                                                                
       EXCEPTION                                                                  
         ... WHEN OTHERS THEN                                                     
                ROLLBACK;                                                         
                v_error_code:=SQLCODE;                                            
                V_ERROR_MESSAGE:=SQLERRM;                                         
                INSERT INTO ........                                              
          END;                                                                    
   八、调用外围环境                                                          
        SQLPLUS                                                                   
        PROCEDURE BUILDER                                                         
        DEVELOPER 2000                                                            
        OTHER .........                                                           
        ---- 也就是把ERROR NUMBER和MESSAGE输出到SCREEN。                          
   九、使用RAISE_APPLICATION_ERROR                                           
        EXCEPTION                                                                 
          WHEN NO_DATA_FOUND THEN                                                 
            RAISE_APPLICATION_ERROR(-20201,'NO MATCH RECORD YOU WANNA');          
      END;                                                                          
        当然,RAISE_APPLICATION_ERRO也是可以放在EXECUTE区的,IF 。。。 THEN       
                       RAISE_APPLICATION_ERRO();                                 
                  END IF;                                                        

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=449107

posted on 2007-01-30 09:52  莫问奴归处  阅读(2125)  评论(0编辑  收藏  举报
轩轩娃