📜  PL/SQL 中的异常处理

📅  最后修改于: 2022-05-13 01:55:46.752000             🧑  作者: Mango

PL/SQL 中的异常处理

异常是破坏正常程序指令流的错误。 PL/SQL 为我们提供了引发异常的异常块,从而帮助程序员找出故障并解决它。

PL/SQL 中定义了两种类型的异常

  1. 用户定义的异常。
  2. 系统定义的异常。

编写异常的语法

WHEN exception THEN 
    statement;

笔记:
当其他关键字应仅在异常处理块的末尾使用时,因为稍后将不执行异常处理部分,因为控件将在执行 WHEN OTHERS 后退出块。

  1. 系统定义的异常:

  2. 这些异常是在 PL/SQL 中预定义的,当违反某些数据库规则时会引发这些异常。
    系统定义的异常进一步分为两类:
    1. 命名系统异常。
    2. 未命名的系统异常。

  • 命名系统异常:它们具有系统预定义的名称,如 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);
    

    1. 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
      
    2. 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
      
    3. 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)
      
    4. 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
      

  • 未命名的系统异常: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 开发人员,如果您在其他地方运行此代码,输出顺序可能会改变。

    异常处理中的范围规则

    1. 我们不能两次声明异常,但我们可以在两个不同的块中声明相同的异常。
    2. 块内声明的异常对于该块是本地的,对于它的所有子块是全局的。

    由于块只能引用局部或全局异常,封闭块不能引用子块中声明的异常。
    如果我们在子块中重新声明全局异常,则以局部声明为准,即局部范围更大。

    例子:

    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 */
         ...   
       

    从上面的代码我们可以得出结论,异常处理

    1. 通过让我们隔离错误处理例程并因此提供健壮性来提高可读性
    2. 提供可靠性,而不是在每个点检查不同类型的错误,我们可以简单地将它们写入异常块中,如果存在错误,则会引发异常,从而帮助程序员找出错误的类型并最终解决它。

    用途: 可以在在线火车预订系统中找到异常的真实生活用途之一。
    在填写车站代码订票时,如果我们输入错误的代码,它会向我们显示该代码在数据库中不存在的异常。

    参考:您可以在此处找到所有预定义异常的列表。
    预定义异常的总数