zoukankan      html  css  js  c++  java
  • PL/SQL异常处理方法

    PL/SQL异常处理方法
     
    1:什么是异常处理:
    PL/SQL提供一个功能去处理异常,在PL/SQL块中叫做异常处理,使用异常处理我们能够测试代码和避免异常退出。
    PL/SQL异常信息包含三个部分:
          1:异常类型
           2:错误代码
           3:错误信息
    通过处理异常我们能够确保PL/SQL块不突然的异常退出。
     
    2:异常处理的结构
    DECLARE
       Declaration section 
     BEGIN 
       Exception section 
     EXCEPTION 
     WHEN ex_name1 THEN 
        -Error handling statements 
     WHEN ex_name2 THEN 
        -Error handling statements 
     WHEN Others THEN 
       -Error handling statements 
    END; 
    例子:
    when others 能够截取所有的异常,一般放在异常处理的最后。
    以上代码解释: 当异常出现的时候,oracle寻找合适的when的异常类型执行异常。
     
    异常处理的好例子: 除0异常:
    [sql] 
    SQL> DECLARE  
      2     stock_price NUMBER := 9.73;  
      3     net_earnings NUMBER := 0;  
      4     pe_ratio NUMBER;  
      5  BEGIN  
      6  -- Calculation might cause division-by-zero error.  
      7     pe_ratio := stock_price / net_earnings;  
      8     DBMS_OUTPUT.PUT_LINE('Price/earnings ratio = ' || pe_ratio);  
      9  EXCEPTION  -- exception handlers begin  
     10  -- Only one of the WHEN blocks is executed.  
     11     WHEN ZERO_DIVIDE THEN  -- handles 'division by zero' error  
     12        DBMS_OUTPUT.PUT_LINE('Company must have had zero earnings.');  
     13        pe_ratio := NULL;  
     14     WHEN OTHERS THEN  -- handles all other errors  
     15        DBMS_OUTPUT.PUT_LINE('Some other kind of error occurred.');  
     16        pe_ratio := NULL;  
     17  END;  -- exception handlers and block end here  
     18  /  
      
    PL/SQL 过程已成功完成。  
      
    SQL> set serveroutput on  
    SQL> /  
    Company must have had zero earnings.  
    为了避免除0可以这样:
    [sql] 
    DECLARE  
       stock_price NUMBER := 9.73;  
       net_earnings NUMBER := 0;  
       pe_ratio NUMBER;  
    BEGIN  
       pe_ratio :=  
          CASE net_earnings  
             WHEN 0 THEN NULL  
             ELSE stock_price / net_earnings  
          end;  
    END;  
    /  
     
     
    如果有一个嵌套的PL/SQL块像这样:
     DELCARE
       Declaration section 
     BEGIN
        DECLARE
          Declaration section 
        BEGIN 
          Execution section 
        EXCEPTION 
          Exception section 
        END; 
     EXCEPTION
       Exception section 
     END; 
     
    在以上例子中: 如果异常出现在内部的块中,内部异常处理块应该处理这个异常,如果内部处理块没有处理这个
    异常,控制会转移它的上一级的PL/SQL块中,如果上一级也没有对应的异常处理块,程序将错误的结束。
     
    3:异常的类型:
    异常有三种: 
      a:系统命名的异常
      b:系统未命名的异常
      c:用户自定义的异常(这个没用过)
     
    a:系统命名的异常:
    当程序违反关系型数据库规则的时候,oracle的系统异常会自动出现,有一些系统异常出现的比较频繁,它们是预定义
    异常,这样的异常都有一个名字。
    例如:NO_DATA_FOUND 和 ZERO_DIVIDE都有名字的系统异常。
    常见的有:
    Oracle Exception Name  Oracle Error  Explanation
    DUP_VAL_ON_INDEX  ORA-00001  You tried to execute an INSERT or UPDATE statement that has created a duplicate value in a field restricted by a unique index.
    TIMEOUT_ON_RESOURCE  ORA-00051  You were waiting for a resource and you timed out.
    TRANSACTION_BACKED_OUT  ORA-00061  The remote portion of a transaction has rolled back.
    INVALID_CURSOR  ORA-01001  You tried to reference a cursor that does not yet exist. This may have happened because you've executed a FETCH cursor or CLOSE cursor before OPENing the cursor.
    NOT_LOGGED_ON  ORA-01012  You tried to execute a call to Oracle before logging in.
    LOGIN_DENIED  ORA-01017  You tried to log into Oracle with an invalid username/password combination.
    NO_DATA_FOUND  ORA-01403  You tried one of the following:
    You executed a SELECT INTO statement and no rows were returned.
    You referenced an uninitialized row in a table.
    You read past the end of file with the UTL_FILE package.
    TOO_MANY_ROWS  ORA-01422  You tried to execute a SELECT INTO statement and more than one row was returned.
    ZERO_DIVIDE  ORA-01476  You tried to divide a number by zero.
    INVALID_NUMBER  ORA-01722  You tried to execute an SQL statement that tried to convert a string to a number, but it was unsuccessful.
    STORAGE_ERROR  ORA-06500  You ran out of memory or memory was corrupted.
    PROGRAM_ERROR  ORA-06501  This is a generic "Contact Oracle support" message because an internal problem was encountered.
    VALUE_ERROR  ORA-06502  You tried to perform an operation and there was a error on a conversion, truncation, or invalid constraining of numeric or character data.
    CURSOR_ALREADY_OPEN  ORA-06511  You tried to open a cursor that is already open. 以上的例子中有关于命名的异常的使用方法。这里不再说了。
    BEGIN 
      Execution section
    EXCEPTION 
    WHEN NO_DATA_FOUND THEN 
     dbms_output.put_line ('A SELECT...INTO did not return any row.'); 
     END; 
     
    b:未命名系统异常:
    这些系统异常没有名字,这些异常不经常出现,这些异常有错误代码和关联信息。
    有两种方式处理未命名的异常:
            方法一: 使用WHEN OTHERS 异常处理
            方法二:给一个异常 关联异常代码和名称 ,然后像命名异常一样使用它。
    方法一异常没有目标性,下面说明方法二:
    使用Pragma 调用 EXCEPTION_INIT关联一个预定义的oracle错误号到程序定义的异常
    下面是个demo:
    DECLARE 
       exception_name EXCEPTION; 
       PRAGMA 
       EXCEPTION_INIT (exception_name, Err_code); 
    BEGIN 
    Execution section
    EXCEPTION
      WHEN exception_name THEN
         handle the exception
    END;
    先声明异常名称,然后调用EXCEPTION_INIT函数绑定错误号码和异常名称, 绑定后这个异常名称就可以像命名式异常那样用了。
    例如:
    [sql] 
    SQL> DECLARE  
      2         e_insert_excep  EXCEPTION;  --定义异常名称  
      3         PRAGMA EXCEPTION_INIT(e_insert_excep,-01400);   -- 关联异常名称和异  
    常号  
      4         BEGIN  
      5         INSERT INTO departments (department_id,department_name) VALUES(280,N  
    ULL);  
      6         EXCEPTION  
      7          WHEN  e_insert_excep THEN  
      8            DBMS_OUTPUT.PUT_LINE('INSERT OPERATION FAILED');  
      9            DBMS_OUTPUT.PUT_LINE(SQLERRM);  
     10         END;  
     11  /  
    INSERT OPERATION FAILED  
    ORA-01400: 无法将 NULL 插入 ("HR"."DEPARTMENTS"."DEPARTMENT_NAME")  
      
    PL/SQL 过程已成功完成。  
     
    首先定义一个 e_insert_excep异常名称, 然后调用 EXCEPTION_INIT函数绑定这个异常名称,最后出现异常输出错误信息。
    SQLERRM的信息如下: ORA-01400: 无法将 NULL 插入 ("HR"."DEPARTMENTS"."DEPARTMENT_NAME")
     
     
    需要关联 数字和自定义的错误名称;
    两个变量: SQLCODE  错误代码
                SQLERRM  错误内容
       当抛出异常的时候,这两个变量会被自动填充,可以获取这两个的值来判断
       错误:         
     
    3:用户自定义异常:
    先看三个例子再解释:
    ex1:
    [sql] 
    SQL>  DECLARE  
      2       v_deptno NUMBER := 500;  
      3       v_name  VARCHAR2(20)  :='Testing';  
      4       e_invalid_department EXCEPTION;  
      5    BEGIN  
      6       UPDATE departments SET department_name = v_name  
      7       WHERE  department_id = v_deptno;  
      8  
      9       IF SQL%NOTFOUND THEN  
     10          RAISE  e_invalid_department;  
     11       END IF;  
     12       COMMIT;  
     13     EXCEPTION  
     14       WHEN e_invalid_department THEN  
     15        DBMS_OUTPUT.PUT_LINE('No such department id');  
     16    END;  
     17  /  
    No such department id  
      
    PL/SQL 过程已成功完成。  
     
    关于RAISE_APPLICATION_ERROR  过程;
    语法:
    raise_application_error(error_number,message[,{TRUE|FALSE}]);
    可以通过这个过程定义一个自己的异常号和 异常信息
    注意: error_number是介于: -20000..-20999的数字,message是一个
    字符串最大长度为2k
    [sql] 
    SQL> DECLARE  
      2      v_deptno NUMBER := 500;  
      3      v_name VARCHAR2(20) := 'Testing';  
      4      e_invalid_department EXCEPTION;  --定义一个异常  
      5      PRAGMA  EXCEPTION_INIT(e_invalid_department,-20188);    --把异常和异常  
    号绑定;  
      6  BEGIN  
      7      UPDATE  departments  
      8      SET  department_name =v_name  
      9      WHERE department_id = v_deptno;  
     10  
     11      IF SQL%NOTFOUND THEN  
     12  
     13         RAISE_APPLICATION_ERROR(-20188,'I write my error message here !');  
      
     14     END IF;  
     15     COMMIT;  
     16   EXCEPTION  
     17          WHEN  e_invalid_department THEN  
     18               DBMS_OUTPUT.PUT_LINE(SQLCODE || '--->' || SQLERRM);  
     19  END;  
     20  
     21  /  
    -20188--->ORA-20188: I write my error message here !  
      
    PL/SQL 过程已成功完成。  
    说明:RAISE_APPLICATION_ERROR ( )
    RAISE_APPLICATION_ERROR是一个内建的存储过程,这个存储过程可以显示用户
    自定义的错误信息和错误号 ,这些错误号是Oracle开放出来供开发者用的,
    范围为:-20000 and -20999
    当使用RAISE_APPLICATION_ERROR的时候,所以以前的事务不提交,自动回滚。
    语法格式如下:
    RAISE_APPLICATION_ERROR (error_number, error_message); 
    使用RAISE_APPLICATION_ERROR步骤如下:
     
    1:首先在Declaration区域自定义一个exception,
    2:在特定的逻辑规则下出现 Raise 用户自定义的exception
    3:最后 catch 这个异常,在捕获后使用RAISE_APPLICATION_ERROR这个过程链接
    自定义的错误号和错误信息。
     
    重点: 关于异常的传播机制:
    比较以下三个例子就会明白异常的传播机制:
    ex1:
    [sql] 
    CREATE OR REPLACE PROCEDURE add_more_departments(p_name1 VARCHAR2, p_mgr1 NUMBER, p_loc1 NUMBER,p_name2 VARCHAR2, p_mgr2 NUMBER, p_loc2 NUMBER) IS  
    BEGIN  
      INSERT INTO departments(department_id,department_name,manager_id,location_id)VALUES(departments_seq.NEXTVAL,p_name1,p_mgr1,p_loc1);  
      DBMS_OUTPUT.PUT_LINE('Add Dept: ' || p_name1);  
       
     INSERT INTO departments(department_id,department_name,manager_id,location_id)VALUES(departments_seq.NEXTVAL,p_name2,p_mgr2,p_loc2);  
     DBMS_OUTPUT.PUT_LINE('Add Dept: ' || p_name2);  
      
    EXCEPTION   
          WHEN OTHERS THEN  
             DBMS_OUTPUT.PUT_LINE('Err: adding dept:');  
    END;  
    /          
      
    CREATE OR REPLACE PROCEDURE create_more_departments  
    IS  
    BEGIN  
      add_more_departments('Media',100,1800,'Editing',99,1800);  
    END;  
    /  
      
    BEGIN  
    create_more_departments;  
    END;   
     
    这个例子中 99这条记录在表中本来就有,现在再次进行插入,会冲突报错
    执行结果:以上两个insert 只有第一个插入进去了,第二个插入失败。
    执行结果如下:
    SQL> /
    Add Dept: Media
    Err: adding dept:
    PL/SQL 过程已成功完成。
     
     
    DEPARTMENT_ID DEPARTMENT_NAME
    ------------- ------------------------------
              230 IT Helpdesk
              240 Government Sales
              250 Retail Sales
              260 Recruiting
              270 Payroll
              340 Media
     
     
    已选择28行。
    只有第一条被插入
    ex2:
    在做第二个例子前先把上次的实验结果删除掉:
    delete from departments where department_id >270;
    select department_id,department_name from departments order by 1;
     
    [sql] 
    CREATE OR REPLACE PROCEDURE add_more_departments(p_name1 VARCHAR2, p_mgr1 NUMBER, p_loc1 NUMBER,p_name2 VARCHAR2, p_mgr2 NUMBER, p_loc2 NUMBER) IS  
    BEGIN  
      INSERT INTO departments(department_id,department_name,manager_id,location_id)VALUES(departments_seq.NEXTVAL,p_name1,p_mgr1,p_loc1);  
      DBMS_OUTPUT.PUT_LINE('Add Dept: ' || p_name1);  
       
     INSERT INTO departments(department_id,department_name,manager_id,location_id)VALUES(departments_seq.NEXTVAL,p_name2,p_mgr2,p_loc2);  
     DBMS_OUTPUT.PUT_LINE('Add Dept: ' || p_name2);  
      
    END;  
    /          
      
    CREATE OR REPLACE PROCEDURE create_more_departments  
    IS  
    BEGIN  
      add_more_departments('Media',100,1800,'Editing',99,1800);  
    END;  
    /   
      
    BEGIN  
    create_more_departments;  
    END;   
     
    执行结果如下:
    SQL> BEGIN
      2  create_more_departments;
      3  END;
      4  /
    Add Dept: Media
    BEGIN
    *
    第 1 行出现错误:
    ORA-02291: 违反完整约束条件 (HR.DEPT_MGR_FK) - 未找到父项关键字
    ORA-06512: 在 "HR.ADD_MORE_DEPARTMENTS", line 6
    ORA-06512: 在 "HR.CREATE_MORE_DEPARTMENTS", line 4
    ORA-06512: 在 line 2
     
    查询结果:
     
     
    DEPARTMENT_ID DEPARTMENT_NAME
    ------------- ------------------------------
              230 IT Helpdesk
              240 Government Sales
              250 Retail Sales
              260 Recruiting
              270 Payroll
    这次没有异常处理,把异常处理去掉了。
    这次的执行结果: 两条记录一条记录也没有插入进去。 
    ex3:
    delete from departments where department_id >270;
    select department_id,department_name from departments order by 1;
     
    [sql] 
    CREATE OR REPLACE PROCEDURE add_more_departments(p_name1 VARCHAR2, p_mgr1 NUMBER, p_loc1 NUMBER,p_name2 VARCHAR2, p_mgr2 NUMBER, p_loc2 NUMBER) IS  
    BEGIN  
      INSERT INTO departments(department_id,department_name,manager_id,location_id)VALUES(departments_seq.NEXTVAL,p_name1,p_mgr1,p_loc1);  
      DBMS_OUTPUT.PUT_LINE('Add Dept: ' || p_name1);  
       
     INSERT INTO departments(department_id,department_name,manager_id,location_id)VALUES(departments_seq.NEXTVAL,p_name2,p_mgr2,p_loc2);  
     DBMS_OUTPUT.PUT_LINE('Add Dept: ' || p_name2);  
      
    END;  
    /          
      
    CREATE OR REPLACE PROCEDURE create_more_departments  
    IS  
    BEGIN  
      add_more_departments('Media',100,1800,'Editing',99,1800);  
    EXCEPTION   
          WHEN OTHERS THEN  
             DBMS_OUTPUT.PUT_LINE('Errors have happend');    
    END;  
    /   
    BEGIN  
    create_more_departments;  
    END;   
     
    执行结果:
    SQL> BEGIN
      2  create_more_departments;
      3  END;
      4  /
    Add Dept: Media
    Errors have happend
     
    PL/SQL 过程已成功完成。
    查询结果:
    DEPARTMENT_ID DEPARTMENT_NAME
    ------------- ------------------------------
              230 IT Helpdesk
              240 Government Sales
              250 Retail Sales
              260 Recruiting
              270 Payroll
              400 Media
     
    这次把异常处理放在了最外面:调用的最外面:
    执行结果如下:
    执行成功的第一条语句被成功插入,第二条被捕获。
  • 相关阅读:
    Git切换分支
    JS中字符串那些事~
    将博客搬至CSDN
    MFC默认窗口类名称
    Windows下使用vim编写代码,使用nmake编译代码,使用vs来调试代码
    从CWnd::GetSafeHwnd实现得到的知识
    ctags使用
    MCI支持的格式在注册表中的位置
    注意!!一定要谨慎使用c/c++原生指针
    MFC模态对话框程序不响应OnIdle
  • 原文地址:https://www.cnblogs.com/BillLei/p/4605144.html
Copyright © 2011-2022 走看看