整理课堂笔记plsql orcale异常

it2022-05-05  162

 

1>>>>>异常错误处理

1 >预定义的异常处理

预定义说明的部分 ORACLE 异常错误对这种异常情况的处理,只需在PL/SQL块的异常处理部分,直接引用相应的异常情况名,并对其完成相应的异常错误处理即可。例1:更新指定员工工资,如工资小于1500,则加100;DECLARE v_empno employees.employee_id%TYPE := &empno; v_sal employees.salary%TYPE;BEGIN SELECT salary INTO v_sal FROM employees WHERE employee_id = v_empno; IF v_sal<=1500 THEN UPDATE employees SET salary = salary + 100 WHERE employee_id=v_empno; DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'员工工资已更新!'); ELSE DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'员工工资已经超过规定值!'); END IF;EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('数据库中没有编码为'||v_empno||'的员工'); WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('程序运行错误!请使用游标'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);END;

2> 非预定义的异常处理对于这类异常情况的处理,首先必须对非定义的ORACLE错误进行定义。步骤如下:1. 在PL/SQL 块的定义部分定义异常情况:<异常情况> EXCEPTION;2. 将其定义好的异常情况,与标准的ORACLE错误联系起来,使用EXCEPTION_INIT语句:PRAGMA EXCEPTION_INIT(<异常情况>, <错误代码>);3. 在PL/SQL 块的异常情况处理部分对异常情况做出相应的处理。例2:删除指定部门的记录信息,以确保该部门没有员工。INSERT INTO departments VALUES(50, 'FINANCE', 'CHICAGO');

DECLARE v_deptno departments.department_id%TYPE := &deptno; deptno_remaining EXCEPTION; PRAGMA EXCEPTION_INIT(deptno_remaining, -2292);BEGIN DELETE FROM departments WHERE department_id = v_deptno;EXCEPTION WHEN deptno_remaining THEN DBMS_OUTPUT.PUT_LINE('违反数据完整性约束!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);END;3> 用户自定义的异常处理当与一个异常错误相关的错误出现时,就会隐含触发该异常错误。用户定义的异常错误是通过显式使用 RAISE 语句来触发。当引发一个异常错误时,控制就转向到 EXCEPTION块异常错误部分,执行错误处理代码。对于这类异常情况的处理,步骤如下:1. 在PL/SQL 块的定义部分定义异常情况:<异常情况> EXCEPTION;2. RAISE <异常情况>;3. 在PL/SQL 块的异常情况处理部分对异常情况做出相应的处理。例3:更新指定员工工资,增加100;DECLARE v_empno employees.employee_id%TYPE :=&empno; no_result EXCEPTION;BEGIN UPDATE employees SET salary = salary+100 WHERE employee_id = v_empno; IF SQL%NOTFOUND THEN RAISE no_result; END IF;EXCEPTION WHEN no_result THEN DBMS_OUTPUT.PUT_LINE('你的数据更新语句失败了!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);END;5.1.4 用户定义的异常处理调用DBMS_STANDARD(ORACLE提供的包)包所定义的RAISE_APPLICATION_ERROR过程,可以重新定义异常错误消息,它为应用程序提供了一种与ORACLE交互的方法。RAISE_APPLICATION_ERROR 的语法如下: RAISE_APPLICATION_ERROR(error_number,error_message,[keep_errors] ); 这里的error_number 是从 –20,000 到 –20,999 之间的参数, error_message 是相应的提示信息(< 2048 字节),keep_errors 为可选,如果keep_errors =TRUE ,则新错误将被添加到已经引发的错误列表中。如果keep_errors=FALSE(缺省),则新错误将替换当前的错误列表。例4:创建一个函数get_salary, 该函数检索指定部门的工资总和,其中定义了-20991和-20992号错误,分别处理参数为空和非法部门代码两种错误:CREATE TABLE errlog( Errcode NUMBER, Errtext CHAR(40));

CREATE OR REPLACE FUNCTION get_salary(p_deptno NUMBER)RETURN NUMBERAS v_sal NUMBER;BEGIN IF p_deptno IS NULL THEN RAISE_APPLICATION_ERROR(-20991, ’部门代码为空’); ELSIF p_deptno<0 THEN RAISE_APPLICATION_ERROR(-20992, ’无效的部门代码’); ELSE SELECT SUM(employees.salary) INTO v_sal FROM employees WHERE employees.department_id=p_deptno; RETURN v_sal; END IF;END;

DECLARE V_salary NUMBER(7,2); V_sqlcode NUMBER; V_sqlerr VARCHAR2(512); Null_deptno EXCEPTION; Invalid_deptno EXCEPTION; PRAGMA EXCEPTION_INIT(null_deptno,-20991); PRAGMA EXCEPTION_INIT(invalid_deptno, -20992);BEGIN V_salary :=get_salary(10); DBMS_OUTPUT.PUT_LINE('10号部门工资:' || TO_CHAR(V_salary));

BEGIN V_salary :=get_salary(-10); EXCEPTION WHEN invalid_deptno THEN V_sqlcode :=SQLCODE; V_sqlerr :=SQLERRM; INSERT INTO errlog(errcode, errtext) VALUES(v_sqlcode, v_sqlerr); COMMIT; END inner1;

V_salary :=get_salary(20); DBMS_OUTPUT.PUT_LINE('部门号为20的工资为:'||TO_CHAR(V_salary));

BEGIN V_salary :=get_salary(NULL); END inner2;

V_salary := get_salary(30); DBMS_OUTPUT.PUT_LINE('部门号为30的工资为:'||TO_CHAR(V_salary));

EXCEPTION WHEN null_deptno THEN V_sqlcode :=SQLCODE; V_sqlerr :=SQLERRM; INSERT INTO errlog(errcode, errtext) VALUES(v_sqlcode, v_sqlerr); COMMIT; WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);END outer;例5:定义触发器,使用RAISE_APPLICATION_ERROR阻止没有员工姓名的新员式记录插入:CREATE OR REPLACE TRIGGER tr_insert_empBEFORE INSERT ON employeesFOR EACH ROWBEGIN IF :new.first_name IS NULL OR :new.last_name is null THEN RAISE_APPLICATION_ERROR(-20000,'Employee must have a name.'); END IF;END;2 >>>>>>异常错误传播 由于异常错误可以在声明部分和执行部分以及异常错误部分出现,因而在不同部分引发的异常错误也不一样。1 .在执行部分引发异常错误 当一个异常错误在执行部分引发时,有下列情况:a. 如果当前块对该异常错误设置了处理,则执行它并成功完成该块的执行,然后控制转给包含块。b. 如果没有对当前块异常错误设置定义处理器,则通过在包含块中引发它来传播异常错误。然后对该包含块执行步骤1)。2. 在声明部分引发异常错误 如果在声明部分引起异常情况,即在声明部分出现错误,那么该错误就能影响到其它的块。比如在有如下的PL/SQL程序:DECLARE name varchar2(12):='EricHu'; 其它语句BEGIN 其它语句EXCEPTION WHEN OTHERS THEN 其它语句END; 例子中,由于Abc number(3)=’abc’; 出错,尽管在EXCEPTION中说明了WHEN OTHERS THEN语句,但WHEN OTHERS THEN也不会被执行。 但是如果在该错误语句块的外部有一个异常错误,则该错误能被抓住,如:BEGIN DECLARE name varchar2(12):='EricHu'; 其它语句 BEGIN 其它语句 EXCEPTION WHEN OTHERS THEN 其它语句 END;EXCEPTIONWHEN OTHERS THEN 其它语句END;3. 异常错误处理编程 在一般的应用处理中,建议程序人员要用异常处理,因为如果程序中不声明任何异常处理,则在程序运行出错时,程序就被终止,并且也不提示任何信息。下面是使用系统提供的异常来编程的例子。4 .在 PL/SQL 中使用 SQLCODE, SQLERRM异常处理函数 由于ORACLE 的错信息最大长度是512字节,为了得到完整的错误提示信息,我们可用 SQLERRM和 SUBSTR 函数一起得到错误提示信息,方便进行错误,特别是如果WHEN OTHERS异常处理器时更为方便。SQLCODE 返回遇到的Oracle错误号,SQLERRM 返回遇到的Oracle错误信息.如: SQLCODE=-100 è SQLERRM=’no_data_found ‘SQLCODE=0 è SQLERRM=’normal, successfual completion’例6. 将ORACLE错误代码及其信息存入错误代码表CREATE TABLE errors (errnum NUMBER(4), errmsg VARCHAR2(100));

DECLARE err_msg VARCHAR2(100);BEGIN FOR err_num IN -100 .. 0 LOOP err_msg := SQLERRM(err_num); INSERT INTO errors VALUES(err_num, err_msg); END LOOP;END;DROP TABLE errors;例7. 查询ORACLE错误代码;BEGIN INSERT INTO employees(employee_id, first_name,last_name,hire_date,department_id) VALUES(2222, 'Eric','Hu', SYSDATE, 20); DBMS_OUTPUT.PUT_LINE('插入数据记录成功!'); INSERT INTO employees(employee_id, first_name,last_name,hire_date,department_id) VALUES(2222, '胡','勇', SYSDATE, 20); DBMS_OUTPUT.PUT_LINE('插入数据记录成功!');EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);END;例8. 利用ORACLE错误代码,编写异常错误处理代码;DECLARE empno_remaining EXCEPTION; PRAGMA EXCEPTION_INIT(empno_remaining, -1);BEGIN INSERT INTO employees(employee_id, first_name,last_name,hire_date,department_id) VALUES(3333, 'Eric','Hu', SYSDATE, 20); DBMS_OUTPUT.PUT_LINE('插入数据记录成功!'); INSERT INTO employees(employee_id, first_name,last_name,hire_date,department_id) VALUES(3333, '胡','勇',SYSDATE, 20); DBMS_OUTPUT.PUT_LINE('插入数据记录成功!');EXCEPTION WHEN empno_remaining THEN DBMS_OUTPUT.PUT_LINE('违反数据完整性约束!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);END;1、异常的优点    如果没有异常,在程序中,应当检查每个命令的成功还是失败,如  BEGIN  SELECT ...  -- check for ’no data found’ error  SELECT ...  -- check for ’no data found’ error  SELECT ...  -- check for ’no data found’ error  这种实现的方法缺点在于错误处理没有与正常处理分开,可读性差,使用异常,可以方便处理错误,而且异常处理程序与正常的事务逻辑分开,提高了可读性,如  BEGIN  SELECT ...  SELECT ...  SELECT ...  ...  EXCEPTION  WHEN NO_DATA_FOUND THEN -- catches all ’no data found’ errors    2、异常的分类    有两种类型的异常,一种为内部异常,一种为用户自定义异常,内部异常是执行期间返回到PL/SQL块的ORACLE错误或由PL/SQL代码的某操作引起的错误,如除数为零或内存溢出的情况。用户自定义异常由开发者显示定义,在PL/SQL块中传递信息以控制对于应用的错误处理。    每当PL/SQL违背了ORACLE原则或超越了系统依赖的原则就会隐式的产生内部异常。因为每个ORACLE错误都有一个号码并且在PL/SQL中异常通过名字处理,ORACLE提供了预定义的内部异常。如SELECT INTO 语句不返回行时产生的ORACLE异常NO_DATA_FOUND。对于预定义异常,现将最常用的异常列举如下:  exception  oracle error  sqlcode value  condition  no_data_found              ora-01403  +100  select into 语句没有符合条件的记录返回  too_many_rows  ora-01422  -1422  select into 语句符合条件的记录有多条返回  dup_val_on_index  ora-00001  -1  对于数据库表中的某一列,该列已经被限制为唯一索引,程序试图存储两个重复的值  value_error  ora-06502  -6502  在转换字符类型,截取或长度受限时,会发生该异常,如一个字符分配给一个变量,而该变量声明的长度比该字符短,就会引发该异常  storage_error  ora-06500  -6500  内存溢出  zero_divide  ora-01476  -1476  除数为零  case_not_found  ora-06592  -6530  对于选择case语句,没有与之相匹配的条件,同时,也没有else语句捕获其他的条件  cursor_already_open  ora-06511  -6511  程序试图打开一个已经打开的游标  timeout_on_resource  ora-00051  -51  系统在等待某一资源,时间超时    如果要处理未命名的内部异常,必须使用OTHERS异常处理器或PRAGMA EXCEPTION_INIT 。PRAGMA由编译器控制,或者是对于编译器的注释。PRAGMA在编译时处理,而不是在运行时处理。EXCEPTION_INIT告诉编译器将异常名与ORACLE错误码结合起来,这样可以通过名字引用任意的内部异常,并且可以通过名字为异常编写一适当的异常处理器。    在子程序中使用EXCEPTION_INIT的语法如下:  PRAGMA EXCEPTION_INIT(exception_name, -Oracle_error_number);    在该语法中,异常名是声明的异常,下例是其用法:  DECLARE  deadlock_detected EXCEPTION;  PRAGMA EXCEPTION_INIT(deadlock_detected, -60);  BEGIN  ... -- Some operation that causes an ORA-00060 error  EXCEPTION  WHEN deadlock_detected THEN  -- handle the error  END;    对于用户自定义异常,只能在PL/SQL块中的声明部分声明异常,异常的名字由EXCEPTION关键字引入:  reserved_loaned Exception    产生异常后,控制传给了子程序的异常部分,将异常转向各自异常控制块,必须在代码中使用如下的结构处理错误:  Exception  When exception1 then  Sequence of statements;  When exception2 then  Sequence of statements;  When others then    3、异常的抛出    由三种方式抛出异常    1. 通过PL/SQL运行时引擎    2. 使用RAISE语句    3. 调用RAISE_APPLICATION_ERROR存储过程    当数据库或PL/SQL在运行时发生错误时,一个异常被PL/SQL运行时引擎自动抛出。异常也可以通过RAISE语句抛出  RAISE exception_name;    显式抛出异常是程序员处理声明的异常的习惯用法,但RAISE不限于声明了的异常,它可以抛出任何任何异常。例如,你希望用TIMEOUT_ON_RESOURCE错误检测新的运行时异常处理器,你只需简单的在程序中使用下面的语句:  RAISE TIMEOUT_ON_RESOUCE;    比如下面一个订单输入的例子,若当订单小于库存数量,则抛出异常,并且捕获该异常,处理异常  DECLARE  inventory_too_low EXCEPTION;    ---其他声明语句  BEGIN  IF order_rec.qty>inventory_rec.qty THEN  RAISE inventory_too_low;  END IF  EXCEPTION  WHEN inventory_too_low THEN  order_rec.staus:='backordered';  END;    RAISE_APPLICATION_ERROR内建函数用于抛出一个异常并给异常赋予一个错误号以及错误信息。自定义异常的缺省错误号是+1,缺省信息是User_Defined_Exception。RAISE_APPLICATION_ERROR函数能够在pl/sql程序块的执行部分和异常部分调用,显式抛出带特殊错误号的命名异常。  Raise_application_error(error_number,message[,true,false]))    错误号的范围是-20,000到-20,999。错误信息是文本字符串,最多为2048字节。TRUE和FALSE表示是添加(TRUE)进错误堆(ERROR STACK)还是覆盖(overwrite)错误堆(FALSE)。缺省情况下是FALSE。    如下代码所示:  IF product_not_found THEN  RAISE_APPLICATION_ERROR(-20123,'Invald product code' TRUE);  END IF;    4、异常的处理    PL/SQL程序块的异常部分包含了程序处理错误的代码,当异常被抛出时,一个异常陷阱就自动发生,程序控制离开执行部分转入异常部分,一旦程序进入异常部分就不能再回到同一块的执行部分。下面是异常部分的一般语法:  EXCEPTION  WHEN exception_name THEN  Code for handing exception_name  [WHEN another_exception THEN  Code for handing another_exception]  [WHEN others THEN  code for handing any other exception.]    用户必须在独立的WHEN子串中为每个异常设计异常处理代码,WHEN OTHERS子串必须放置在最后面作为缺省处理器处理没有显式处理的异常。当异常发生时,控制转到异常部分,ORACLE查找当前异常相应的WHEN..THEN语句,捕捉异常,THEN之后的代码被执行,如果错误陷阱代码只是退出相应的嵌套块,那么程序将继续执行内部块END后面的语句。如果没有找到相应的异常陷阱,那么将执行WHEN OTHERS。在异常部分WHEN 子串没有数量限制。  EXCEPTION  WHEN inventory_too_low THEN  order_rec.staus:='backordered';  replenish_inventory(inventory_nbr=>  inventory_rec.sku,min_amount=>order_rec.qty-inventory_rec.qty);  WHEN discontinued_item THEN  --code for discontinued_item processing  WHEN zero_divide THEN  --code for zero_divide  WHEN OTHERS THEN  --code for any other exception  END;    当异常抛出后,控制无条件转到异常部分,这就意味着控制不能回到异常发生的位置,当异常被处理和解决后,控制返回到上一层执行部分的下一条语句。  BEGIN  DECLARE  bad_credit exception;  BEGIN  RAISE bad_credit;  --发生异常,控制转向;  EXCEPTION  WHEN bad_credit THEN  dbms_output.put_line('bad_credit');  END;  --bad_credit异常处理后,控制转到这里  EXCEPTION  WHEN OTHERS THEN    --控制不会从bad_credit异常转到这里    --因为bad_credit已被处理    END;    当异常发生时,在块的内部没有该异常处理器时,控制将转到或传播到上一层块的异常处理部分。    BEGIN  DECLARE ---内部块开始    bad_credit exception;  BEGIN  RAISE bad_credit;    --发生异常,控制转向;  EXCEPTION  WHEN ZERO_DIVIDE THEN --不能处理bad_credite异常  dbms_output.put_line('divide by zero error');    END --结束内部块    --控制不能到达这里,因为异常没有解决;    --异常部分    EXCEPTION  WHEN OTHERS THEN  --由于bad_credit没有解决,控制将转到这里  END;    5、异常的传播    没有处理的异常将沿检测异常调用程序传播到外面,当异常被处理并解决或到达程序最外层传播停止。在声明部分抛出的异常将控制转到上一层的异常部分。    BEGIN  executable statements  BEGIN  today DATE:='SYADATE'; --ERRROR    BEGIN --内部块开始  dbms_output.put_line('this line will not execute');  EXCEPTION  WHEN OTHERS THEN    --异常不会在这里处理    END;--内部块结束  EXCEPTION  WHEN OTHERS THEN    处理异常    END

转载于:https://www.cnblogs.com/lixiaopan/p/6122624.html


最新回复(0)