PL/SQL 中的异常处理
异常是破坏正常程序指令流的错误。 PL/SQL 为我们提供了引发异常的异常块,从而帮助程序员找出故障并解决它。
PL/SQL 中定义了两种类型的异常
- 用户定义的异常。
- 系统定义的异常。
编写异常的语法
WHEN exception THEN
statement;
DECLARE
declarations section;
BEGIN
executable command(s);
EXCEPTION
WHEN exception1 THEN
statement1;
WHEN exception2 THEN
statement2;
[WHEN others THEN]
/* default exception handling code */
END;
笔记:
当其他关键字应仅在异常处理块的末尾使用时,因为稍后将不执行异常处理部分,因为控件将在执行 WHEN OTHERS 后退出块。
- 系统定义的异常:
- 命名系统异常。
- 未命名的系统异常。
这些异常是在 PL/SQL 中预定义的,当违反某些数据库规则时会引发这些异常。
系统定义的异常进一步分为两类:
- 命名系统异常:它们具有系统预定义的名称,如 ACCESS_INTO_NULL、DUP_VAL_ON_INDEX、LOGIN_DENIED 等。列表很大。
所以我们将讨论一些最常用的异常:
让我们创建一个表极客。
create table geeks(g_id int , g_name varchar(20), marks int); insert into geeks values(1, 'Suraj',100); insert into geeks values(2, 'Praveen',97); insert into geeks values(3, 'Jessie', 99);
- NO_DATA_FOUND :当 SELECT INTO 语句不返回任何行时引发。例如:
DECLARE temp varchar(20); BEGIN SELECT g_id into temp from geeks where g_name='GeeksforGeeks'; exception WHEN no_data_found THEN dbms_output.put_line('ERROR'); dbms_output.put_line('there is no name as'); dbms_output.put_line('GeeksforGeeks in geeks table'); end;
输出:
ERROR there is no name as GeeksforGeeks in geeks table
- TOO_MANY_ROWS :当 SELECT INTO 语句返回多行时引发。
DECLARE temp varchar(20); BEGIN -- raises an exception as SELECT -- into trying to return too many rows SELECT g_name into temp from geeks; dbms_output.put_line(temp); EXCEPTION WHEN too_many_rows THEN dbms_output.put_line('error trying to SELECT too many rows'); end;
输出:
error trying to SELECT too many rows
- VALUE_ERROR :当执行导致算术、数字、字符串、转换或约束错误的语句时,会引发此错误。此错误主要是由于程序员错误或无效数据输入造成的。
DECLARE temp number; BEGIN SELECT g_name into temp from geeks where g_name='Suraj'; dbms_output.put_line('the g_name is '||temp); EXCEPTION WHEN value_error THEN dbms_output.put_line('Error'); dbms_output.put_line('Change data type of temp to varchar(20)'); END;
输出:
Error Change data type of temp to varchar(20)
- ZERO_DIVIDE = 在除以零时引发异常。
DECLARE a int:=10; b int:=0; answer int; BEGIN answer:=a/b; dbms_output.put_line('the result after division is'||answer); exception WHEN zero_divide THEN dbms_output.put_line('dividing by zero please check the values again'); dbms_output.put_line('the value of a is '||a); dbms_output.put_line('the value of b is '||b); END;
输出:
dividing by zero please check the values again the value of a is 10 the value of b is 0
- NO_DATA_FOUND :当 SELECT INTO 语句不返回任何行时引发。例如:
- 未命名的系统异常:Oracle 没有为一些称为未命名系统异常的系统异常提供名称。这些异常不经常发生。这些异常有两部分代码和一个关联的消息。
处理这些异常的方法是使用Pragma EXCEPTION_INIT为它们分配名称
句法:PRAGMA EXCEPTION_INIT(exception_name, -error_number);
error_number 是预定义的,具有从 -20000 到 -20999 的负整数范围。
例子:
DECLARE exp exception; pragma exception_init (exp, -20015); n int:=10; BEGIN FOR i IN 1..n LOOP dbms_output.put_line(i*i); IF i*i=36 THEN RAISE exp; END IF; END LOOP; EXCEPTION WHEN exp THEN dbms_output.put_line('Welcome to GeeksforGeeks'); END;
输出:
1 4 9 16 25 36 Welcome to GeeksforGeeks
这种类型的用户可以根据需要创建自己的异常,并使用raise命令显式地引发这些异常。
例子:
- 将非负整数 x 除以 y,使得结果大于或等于 1。
从给定的问题我们可以得出结论,存在两个例外
- 除法为零。
- 如果结果大于或等于 1 意味着 y 小于或等于 x。
DECLARE x int:=&x; /*taking value at run time*/ y int:=&y; div_r float; exp1 EXCEPTION; exp2 EXCEPTION; BEGIN IF y=0 then raise exp1; ELSEIF y > x then raise exp2; ELSE div_r:= x / y; dbms_output.put_line('the result is '||div_r); END IF; EXCEPTION WHEN exp1 THEN dbms_output.put_line('Error'); dbms_output.put_line('division by zero not allowed'); WHEN exp2 THEN dbms_output.put_line('Error'); dbms_output.put_line('y is greater than x please check the input'); END;
Input 1: x = 20 y = 10 Output: the result is 2
Input 2: x = 20 y = 0 Output: Error division by zero not allowed
Input 3: x=20 y = 30 Output: Error y is greater than x please check the input
RAISE_APPLICATION_ERROR :
它用于显示用户定义的错误信息,错误编号范围在-20000 和-20999 之间。当 RAISE_APPLICATION_ERROR 执行时,它返回错误消息和错误代码,看起来与 Oracle 内置错误相同。
例子:
DECLARE
myex EXCEPTION;
n NUMBER :=10;
BEGIN
FOR i IN 1..n LOOP
dbms_output.put_line(i*i);
IF i*i=36 THEN
RAISE myex;
END IF;
END LOOP;
EXCEPTION
WHEN myex THEN
RAISE_APPLICATION_ERROR(-20015, 'Welcome to GeeksForGeeks');
END;
输出:
Error report:
ORA-20015: Welcome to GeeksForGeeks
ORA-06512: at line 13
1
4
9
16
25
36
注意:输出基于 Oracle Sql 开发人员,如果您在其他地方运行此代码,输出顺序可能会改变。
异常处理中的范围规则:
- 我们不能两次声明异常,但我们可以在两个不同的块中声明相同的异常。
- 块内声明的异常对于该块是本地的,对于它的所有子块是全局的。
由于块只能引用局部或全局异常,封闭块不能引用子块中声明的异常。
如果我们在子块中重新声明全局异常,则以局部声明为准,即局部范围更大。
例子:
DECLARE
GeeksforGeeks EXCEPTION;
age NUMBER:=16;
BEGIN
-- sub-block BEGINs
DECLARE
-- this declaration prevails
GeeksforGeeks EXCEPTION;
age NUMBER:=22;
BEGIN
IF age > 16 THEN
RAISE GeeksforGeeks; /* this is not handled*/
END IF;
END;
-- sub-block ends
EXCEPTION
-- Does not handle raised exception
WHEN GeeksforGeeks THEN
DBMS_OUTPUT.PUT_LINE
('Handling GeeksforGeeks exception.');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE
('Could not recognize exception GeeksforGeeks in this scope.');
END;
输出:
Could not recognize exception GeeksforGeeks in this scope.
好处:
- 异常处理对于错误处理非常有用,没有它我们必须在每个点发出命令来检查执行错误:
例子: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 */ ... WHEN ZERO_DIVIDE THEN /* different types of */ WHEN value_error THEN /* errors handled in same block */ ...
从上面的代码我们可以得出结论,异常处理
- 通过让我们隔离错误处理例程并因此提供健壮性来提高可读性。
- 提供可靠性,而不是在每个点检查不同类型的错误,我们可以简单地将它们写入异常块中,如果存在错误,则会引发异常,从而帮助程序员找出错误的类型并最终解决它。
用途: 可以在在线火车预订系统中找到异常的真实生活用途之一。
在填写车站代码订票时,如果我们输入错误的代码,它会向我们显示该代码在数据库中不存在的异常。
参考:您可以在此处找到所有预定义异常的列表。
预定义异常的总数