异常处理后继续执行

时间:2020-05-27 03:22:37

标签: oracle oracle11g oracle10g

我正在编写一个脚本来执行多个唯一查询(不能放在循环中)。添加异常处理程序时,一旦发生第一个错误,便会处理异常,但编译器会退出该块并终止程序。

如何让我的程序处理异常并继续执行下一个查询?

下面是我一直在处理的代码:

DECLARE
  NEW_VAR1 VARCHAR2(20000);
  table_does_not_exist exception;
  PRAGMA EXCEPTION_INIT(table_does_not_exist, -942);
BEGIN
  DBMS_output.put_line('Query 1 Execution');
  EXECUTE IMMEDIATE 'BEGIN
    SELECT "defaultpwd" INTO :out1 from sys.user$ where name="APEX_040000" and substr(spare4,3,40)=rawtohex(utl_raw.cast_to_varchar2(sys.dbms_crypto.hash(utl_raw.cast_to_raw("oracle")||hextoraw(substr(spare4,43,20)), 3))) UNION SELECT "defaultpwd" from sys.user$ where name="APEX_040000" and password="EE7785338B8FFE3D";
  END;'
  USING out NEW_VAR1;
  DBMS_output.put_line(NEW_VAR1);

  DBMS_output.put_line('Query 2 Execution');
  EXECUTE IMMEDIATE 'BEGIN
    SELECT "defaultpwd" INTO :out2 from sys.user$ where name="APEX_040000" and substr(spare4,3,40)=rawtohex(utl_raw.cast_to_varchar2(sys.dbms_crypto.hash(utl_raw.cast_to_raw("oracle")||hextoraw(substr(spare4,43,20)), 3))) UNION SELECT "defaultpwd" from sys.user$ where name="APEX_040000" and password="EE7785338B8FFE3D";
  END;'
  USING out NEW_VAR1;
  DBMS_output.put_line(NEW_VAR1);
EXCEPTION
  WHEN table_does_not_exist THEN
    DBMS_output.put_line('Table does not exist!!');
  WHEN others then
    DBMS_output.put_line('Error!!');
END;

在这种情况下所需的结果将是:

Query 1 Execution
Error!!
Query 2 Execution
Error!!

实际结果是:

Query 1 Execution
Error!!

如果上述代码效率不高,我可以选择执行多种不同查询的替代方法。

3 个答案:

答案 0 :(得分:2)

我认为您需要为每个查询使用不同的异常块。像这样:

    DECLARE
      NEW_VAR1 VARCHAR2(20000);
      table_does_not_exist exception;
      PRAGMA EXCEPTION_INIT(table_does_not_exist, -942);
    BEGIN
      BEGIN
          DBMS_output.put_line('Query 1 Execution');
          EXECUTE IMMEDIATE 'BEGIN
            SELECT "defaultpwd" INTO :out1 from sys.user$ where name="APEX_040000" and    substr(spare4,3,40)=rawtohex(utl_raw.cast_to_varchar2(sys.dbms_crypto.hash(utl_raw.cast_to_raw("oracle")||hextoraw(substr(spare4,43,20)), 3))) UNION SELECT "defaultpwd" from sys.user$ where name="APEX_040000" and password="EE7785338B8FFE3D";
          END;'
          USING out NEW_VAR1;
          DBMS_output.put_line(NEW_VAR1);
      EXCEPTION
          WHEN table_does_not_exist THEN
              DBMS_output.put_line('Table does not exist!!');
          WHEN others then
              DBMS_output.put_line('Error!!');
      END;

      BEGIN
          DBMS_output.put_line('Query 2 Execution');
          EXECUTE IMMEDIATE 'BEGIN
            SELECT "defaultpwd" INTO :out2 from sys.user$ where name="APEX_040000" and substr(spare4,3,40)=rawtohex(utl_raw.cast_to_varchar2(sys.dbms_crypto.hash(utl_raw.cast_to_raw("oracle")||hextoraw(substr(spare4,43,20)), 3))) UNION SELECT "defaultpwd" from sys.user$ where name="APEX_040000" and password="EE7785338B8FFE3D";
          END;'
          USING out NEW_VAR1;
          DBMS_output.put_line(NEW_VAR1);
      EXCEPTION
          WHEN table_does_not_exist THEN
              DBMS_output.put_line('Table does not exist!!');
          WHEN others then
              DBMS_output.put_line('Error!!');
      END;
    END;

答案 1 :(得分:1)

原则上,您必须这样做:

DECLARE
  NEW_VAR1 VARCHAR2(20000);
  table_does_not_exist exception;
  PRAGMA EXCEPTION_INIT(table_does_not_exist, -942);
BEGIN

BEGIN
  DBMS_output.put_line('Query 1 Execution');
  EXECUTE IMMEDIATE 'BEGIN
    SELECT "defaultpwd" INTO :out1 from sys.user$ where name="APEX_040000" and substr(spare4,3,40)=rawtohex(utl_raw.cast_to_varchar2(sys.dbms_crypto.hash(utl_raw.cast_to_raw("oracle")||hextoraw(substr(spare4,43,20)), 3))) UNION SELECT "defaultpwd" from sys.user$ where name="APEX_040000" and password="EE7785338B8FFE3D";
  END;'
  USING out NEW_VAR1;
  DBMS_output.put_line(NEW_VAR1);
EXCEPTION
  WHEN table_does_not_exist THEN
    DBMS_output.put_line('Table does not exist!!');
  WHEN others then
    DBMS_output.put_line('Error!!');
END;

BEGIN
  DBMS_output.put_line('Query 2 Execution');
  EXECUTE IMMEDIATE 'BEGIN
    SELECT "defaultpwd" INTO :out2 from sys.user$ where name="APEX_040000" and substr(spare4,3,40)=rawtohex(utl_raw.cast_to_varchar2(sys.dbms_crypto.hash(utl_raw.cast_to_raw("oracle")||hextoraw(substr(spare4,43,20)), 3))) UNION SELECT "defaultpwd" from sys.user$ where name="APEX_040000" and password="EE7785338B8FFE3D";
  END;'
  USING out NEW_VAR1;
  DBMS_output.put_line(NEW_VAR1);
EXCEPTION
  WHEN table_does_not_exist THEN
    DBMS_output.put_line('Table does not exist!!');
  WHEN others then
    DBMS_output.put_line('Error!!');
END;

END;

或将其放在循环中:

BEGIN

FOR i IN 1..2 LOOP
  BEGIN
    DBMS_output.put_line('Query '||i||' Execution');
    EXECUTE IMMEDIATE ...

  EXCEPTION
    WHEN table_does_not_exist THEN
      DBMS_output.put_line('Table does not exist!!');
    WHEN others then
      DBMS_output.put_line('Error!!');
  END;
END LOOP;

END;

但是您的代码有几个错误/缺陷:

  • 为什么您要运行两次完全相同的查询?
  • EXECUTE IMMEDIATE要求准确返回一个行。由于UNION,这不太可能。
  • 代码必须类似于EXECUTE IMMEDIATE 'SELECT ... FROM ...' INTO NEW_VAR1;
  • 我认为表sys.user$没有任何列defaultpwd(小写)
  • 您应该使用WHEN others then DBMS_output.put_line('Error!!' || SQLERRM);来查看实际错误,否则将其隐藏。如果您是SQL的初学者,那就更糟了
  • 查询的目的是什么,对我来说没有太大意义。如果您想与PASSWORD进行比较,请简单地使用=<>
  • 即使您的查询可行,也没有理由使用动态SQL。使用简单的SELECT ... INTO NEW_VAR1 FROM ...。不需要EXECUTE IMMEDIATE

答案 2 :(得分:0)

动态SQL是否有任何特殊原因?您运行的查询中没有 dynamic

什么是更糟糕的,是那些选择使用UNION,这意味着它们可能会将两行或更多行返回到标量变量中,而这行不通。

>

此外,您滥用了INTO子句。

这类似于您现在正在执行的操作(示例基于Scott的架构):

SQL> declare
  2    l_result varchar2(20);
  3  begin
  4    execute immediate
  5    q'[select deptno into :out1
  6      from emp
  7      where ename = 'SCOTT'
  8     union
  9     select deptno into :out2
 10       from dept
 11       where dname = 'ACCOUNTING']'
 12    using out l_result;
 13
 14    dbms_output.put_line(l_result);
 15  end;
 16  /
declare
*
ERROR at line 1:
ORA-01744: inappropriate INTO
ORA-06512: at line 4


SQL>

如果您对其进行了修复,则在将2行返回到单个too_many_rows变量中时,会得到更糟糕的事情:varchar2

SQL> declare
  2    l_result varchar2(20);
  3  begin
  4    execute immediate
  5    q'[select deptno
  6      from emp
  7      where ename = 'SCOTT'
  8     union
  9     select deptno
 10       from dept
 11       where dname = 'ACCOUNTING']'
 12    into l_result;
 13
 14    dbms_output.put_line(l_result);
 15  end;
 16  /
declare
*
ERROR at line 1:
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 4

由于其中没有动态变化,请切换为纯select(当然,将再次返回too_many_rows):

SQL> declare
  2    l_result varchar2(20);
  3  begin
  4    select deptno
  5      into l_result
  6      from (select deptno
  7              from emp
  8              where ename = 'SCOTT'
  9            union
 10            select deptno
 11              from dept
 12              where dname = 'ACCOUNTING'
 13           );
 14
 15    dbms_output.put_line(l_result);
 16  end;
 17  /
declare
*
ERROR at line 1:
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 4

最后,使用适当的变量,即数组:

SQL> declare
  2    l_result sys.odcivarchar2list;
  3  begin
  4    select deptno
  5      bulk collect into l_result
  6      from (select deptno
  7              from emp
  8              where ename = 'SCOTT'
  9            union
 10            select deptno
 11              from dept
 12              where dname = 'ACCOUNTING'
 13           );
 14
 15    for i in l_result.first .. l_result.last loop
 16      dbms_output.put_line(l_result(i));
 17    end loop;
 18  end;
 19  /
10
20

PL/SQL procedure successfully completed.

SQL>

从遇到的错误开始:正如您已经被告知的那样,您需要将每个语句括在其自己的begin-exception-end块中。像这样的简化示例(“简化”意味着您应该正确处理异常。不要仅仅因为它存在而使用WHEN OTHERS;您宁愿让错误发生,识别并正确处理):

declare
  l_result sys.odcivarchar2list;
begin
  -- start of the first inner begin-exception-end block
  begin
    select deptno
      bulk collect into l_result
      from (select deptno
              from emp
              where ename = 'SCOTT'
            union 
            select deptno
              from dept
              where dname = 'ACCOUNTING'
           );

    for i in l_result.first .. l_result.last loop
      dbms_output.put_line(l_result(i));
    end loop;  
  exception
    when others then
      dbms_output.put_Line('handle it');
  end;
  -- end of the first inner begin-exception-end block

  -- start of the second inner begin-exception-end block
  begin
    null;
  exception
    when others then null;
  end;
  -- end of the second inner begin-exception-end block
end;
/