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

Posted

技术标签:

【中文标题】引发异常的范围,在 PLSQL 代码中处理自己的异常【英文标题】:scope of raise exception, handling your own exceptions in PLSQL code 【发布时间】:2010-01-22 09:45:06 【问题描述】:

我有这个程序:

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的参考之后,我还是有点困惑;基本上,当它为空时,我不想插入,否则我想插入,即使部门号为空(我转为 0)。

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

【问题讨论】:

【参考方案1】:

我想做 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;

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

    任何异常都会使 LOOP 短路。不会再处理更多行 因为您是在 LOOP 中提交的,所以可能很难重新运行该程序,因为您将无法轻松地从中断的地方继续。 在循环内提​​交可能会产生 ORA-1555 或 ORA-1002 错误问题,尤其是当这是一个长时间运行的查询时。

编辑

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

【讨论】:

【参考方案2】:

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

【讨论】:

【参考方案3】:

你的代码有错误:old_emp.num_dep = NULL 不能工作,它总是假的。

假设它是old_emp.num_dep IS NULL,那么我认为你的代码不会按照你的意图工作。绕过 INSERT INTO EMP2 会引发异常。

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

顺便说一句,部门使用 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;

我强烈建议您在代码中添加一些 cmets,因为如果我能找到我上面写的代码,我可能会怀疑存在错误。

【讨论】:

【参考方案4】:

所以,如果我保留异常,它会是这样的:

    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;

那么你会如何重写它,让它看起来不那么“不确定”?这有点乱,我不得不承认。不管怎样,至少你给了我非常实用的见解。 我想看看一些更好的方法,如果你愿意,我很感兴趣。

【讨论】:

以上是关于引发异常的范围,在 PLSQL 代码中处理自己的异常的主要内容,如果未能解决你的问题,请参考以下文章

我在12。21要去一家软件公司笔试,范围大约是VB,JAVA方面的,劳驾帮忙给点相关题目。

在 plsql 异常处理中返回命令无效吗?

用于检索自定义消息而不是异常详细信息的 PLSQL 异常处理

如何处理表或视图不存在异常?

有没有办法在 PLSQL 包中全局处理定义的异常

高阶函数&异常处理