引发异常的范围,在PLSQL代码中处理自己的异常

时间:2010-01-22 09:45:06

标签: oracle plsql exception raise plsqldeveloper

我有这个程序:

create or replace PROCEDURE CONVERTE
IS
    CURSOR oldemployees IS
        SELECT *
        FROM emp1
        WHERE data_saida= NULL;

    new_ndep emp1.num_dep%type;
  bi_inexistente   EXCEPTION;
  dep_inexistente   EXCEPTION;
  employeeNr    emp1.num_empregado%type;

BEGIN
    FOR old_emp IN oldemployees
    LOOP
  employeeNr:= old_emp.num_empregado;
        if (old_emp.bi = NULL) then
        raise bi_inexistente;   
    else  
      IF (old_emp.num_dep>20) THEN
                SELECT ndep_novo INTO new_ndep FROM Converte_dep WHERE ndep_antigo= old_emp.num_dep;
       elsif (old_emp.num_dep = NULL) then
            new_ndep:= 0;
            raise dep_inexistente;    
       end if; 
       INSERT INTO EMP2 VALUES (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida, new_ndep);
       COMMIT;
    end if; 
    end loop; 

EXCEPTION
when bi_inexistente then
  INSERT INTO ERROS VALUES(employeeNr, 'BI Inexistente');
  COMMIT;

when dep_inexistente then
  INSERT INTO ERROS VALUES(employeeNr, 'Departamento Inexistente');
  COMMIT;
end;

我想INSERT INTO EMP2 VALUES(old_emp.bi,old_emp.nome,old_emp.morada,old_emp.data_entrada,old_emp.data_saida,new_ndep);即使在提升dep_inexistente之后,但在阅读了oracle的参考之后,我有点困惑;基本上,当它为null时,我想不要插入,否则我想插入,即使部门号为空(我转向0)。

那么,代码是否正确,或者我应该如何提出异常或处理我案例的预定义异常?

4 个答案:

答案 0 :(得分:3)

  

我想INSERT INTO EMP2 VALUES   (old_emp.bi,old_emp.nome,   old_emp.morada,old_emp.data_entrada,   old_emp.data_saida,new_ndep);甚至   在提升dep_inexistente

之后

诀窍是在执行插入后引发异常。引发的异常是有效的GOTO语句 - 控制流直接转到EXCEPTIONS块。在下面的重写中,我使用了new_dep的设置作为提升异常的信号。您可能知道一些其他业务逻辑使这种方法无效(即,有一些合理的理由说明为什么记录会使部门为零)。在这种情况下,您需要设置一个标志。

create or replace PROCEDURE CONVERTE IS
    CURSOR oldemployees IS
        SELECT *
        FROM emp1
        WHERE data_saida= NULL;
    new_ndep emp1.num_dep%type;
    bi_inexistente   EXCEPTION;
    dep_inexistente   EXCEPTION;
    employeeNr    emp1.num_empregado%type;
BEGIN
    FOR old_emp IN oldemployees
    LOOP
        employeeNr:= old_emp.num_empregado;
        if (old_emp.bi is NULL) then
            raise bi_inexistente;   
        else
            if (old_emp.num_dep>20) THEN
                SELECT ndep_novo INTO new_ndep FROM Converte_dep WHERE ndep_antigo= old_emp.num_dep;
            elsif (old_emp.num_dep is NULL) then
                new_ndep:= 0;
            end if; 
            INSERT INTO EMP2 VALUES (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida, new_ndep);
            COMMIT;
            if new_ndep = 0 then
                raise dep_inexistente;    
            end if;
        end if; 
    end loop; 
EXCEPTION
    when bi_inexistente then
      INSERT INTO ERROS VALUES(employeeNr, 'BI Inexistente');
      COMMIT;
    when dep_inexistente then
      INSERT INTO ERROS VALUES(employeeNr, 'Departamento Inexistente');
      COMMIT;
end;

关于你的一般方法的三件事:

  1. 任何异常都会使LOOP短路。不会处理更多行
  2. 因为你在LOOP中提交,所以可能很难重新运行程序,因为你不能轻易地从你离开的地方继续。
  3. 在循环内部提交会导致ORA-1555或ORA-1002错误出现问题,尤其是在长时间运行的查询中。
  4. 修改

    实际上,您的代码提出了许多关于程序逻辑的问题。远远超过我希望进入这里。我上面列出的三个是一般的“最佳实践”问题,但条件流的详细逻辑看起来不合适。但后来我不知道你正在实施的业务规则。

答案 1 :(得分:2)

我不认为例外应该被用作不优雅的GOTO声明。如果要构建代码,可以使用过程(和子过程)。如果在代码中的某个位置完成工作,只需使用RETURN语句。只有在有意义时才捕获异常。

答案 2 :(得分:1)

您的代码中存在错误:old_emp.num_dep = NULL无效,始终为假。

假设它本来是old_emp.num_dep IS NULL,那么我认为你的代码根据你的意图不起作用。绕过INSERT INTO EMP2会引发异常。

如果这是我的代码,并且逻辑是这样的,你可以决定在部门丢失的情况下插入EMP2不是真正的错误,我不会引发异常。你也没有失去那些信息,因为你总能看到有缺少的部门(即每个以0为部门的emp)

BTW是否有一个特殊的原因为部门使用0?为什么不使用NULL?显然你已经决定让员工没有部门,NULL是公平的代表。

如果你坚持认为emp错过了一个部门实际上是一个错误,但仍然认为无论如何都可以插入EMP,我会考虑这样写:

IF ... THEN
    ... -- ok
END IF;
INSERT INTO EMP2 VALUES (
    old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida,
    NVL(new_ndep, 0)
);
IF new_ndep IS NULL THEN 
    raise dep_inexistente;   
END IF;

我建议你在代码中添加一些注释,因为如果我找到像上面所写的代码,我可能会怀疑是一个错误。

答案 3 :(得分:0)

所以,如果我保留例外,那就像这样:

    create or replace PROCEDURE CONVERTE IS
        CURSOR oldemployees IS
            SELECT *
            FROM emp1
            WHERE data_saida= NULL;
        new_ndep emp1.num_dep%type;
        bi_inexistente   EXCEPTION;
        dep_inexistente   EXCEPTION;
        employeeNr    emp1.num_empregado%type;
    BEGIN
        FOR old_emp IN oldemployees
        LOOP
            employeeNr:= old_emp.num_empregado;
            if (old_emp.bi is NULL) then
                raise bi_inexistente;   
            else
                if (old_emp.num_dep>20) THEN
                    SELECT ndep_novo INTO new_ndep FROM Converte_dep WHERE ndep_antigo= old_emp.num_dep;
                else
                  INSERT INTO EMP2 VALUES (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida,nvl(old_emp.num_dep,0));
                end if;
                if new_ndep is NULL then
                    raise dep_inexistente;    
                end if;
            end if; 
        end loop; 
    EXCEPTION
        when bi_inexistente then
          INSERT INTO ERROS VALUES(employeeNr, 'BI Inexistente');
          COMMIT;
        when dep_inexistente then
          INSERT INTO ERROS VALUES(employeeNr, 'Departamento Inexistente');
          COMMIT;
    end;

或者我可以做所说的事情而不会引发异常;但我仍然需要使用光标。

create or replace
    PROCEDURE CONVERTE2 IS
        CURSOR oldemployees IS
            SELECT *
            FROM emp1
            WHERE data_saida= NULL;
        new_ndep emp1.num_dep%type;
        bi_inexistente   EXCEPTION;
        dep_inexistente   EXCEPTION;
        employeeNr    emp1.num_empregado%type;
        v_error_code    NUMBER:=0;
        v_error_message VARCHAR2(255);

    BEGIN
        FOR old_emp IN oldemployees
        LOOP
            employeeNr:= old_emp.num_empregado;
            if (old_emp.bi is NULL) then
                INSERT INTO ERROS VALUES(employeeNr, 'BI Inexistente');  
            else
                if (old_emp.num_dep>20) THEN
                    SELECT ndep_novo INTO new_ndep FROM Converte_dep WHERE ndep_antigo= old_emp.num_dep;
                else
                  INSERT INTO EMP2 VALUES (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida,nvl(old_emp.num_dep,0));
                end if;
                if new_ndep is NULL then
                    INSERT INTO ERROS VALUES(employeeNr, 'Departamento Inexistente');   
                end if;
            end if; 
        end loop; 
        COMMIT;

    EXCEPTION
        When others Then 
        ROLLBACK;
        /*eventually log something into erro table*/

    end;

那你怎么改写呢,所以看起来不那么“不确定”?我不得不承认,这有点混乱。无论如何,至少你给了我非常实际的见解。 我想看看一些更好的方法,如果你愿意,我很感兴趣。