【问题标题】:scope of raise exception, handling your own exceptions in PLSQL code引发异常的范围,在 PLSQL 代码中处理自己的异常
【发布时间】: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)。

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

【问题讨论】:

    标签: oracle plsql exception raise plsqldeveloper


    【解决方案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;
    

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

    1. 任何异常都会使 LOOP 短路。不会再处理更多行
    2. 因为您是在 LOOP 中提交的,所以可能很难重新运行该程序,因为您将无法轻松地从中断的地方继续。
    3. 在循环内提​​交可能会产生 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;
          

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

          【讨论】:

            猜你喜欢
            • 2018-05-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多