2010-01-22 24 views
1

J'ai cette procédure:champ d'exception relance, la gestion de vos propres exceptions dans le code PLSQL

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; 

Je veux faire INSERT INTO VALEURS EMP2 (old_emp.bi, old_emp.nome, old_emp.morada, old_emp. data_entrada, old_emp.data_saida, new_ndep); même après l'augmentation dep_inexistente, mais après avoir lu la référence d'oracle, je suis un peu confus; Fondamentalement, quand c'est nul, je ne veux pas faire cette insertion, sinon je veux insérer, même lorsque le numéro de département est nul (que je tourne à 0).

Donc, le code est-il correct ou comment dois-je lever mes exceptions ou gérer des exceptions prédéfinies pour mon cas?

Répondre

2

Je ne pense pas que des exceptions devraient être utilisées comme une instruction GOTO inélégante. Si vous souhaitez structurer votre code, vous pouvez utiliser des procédures (et des sous-procédures). Si le travail est effectué à un endroit dans le code, utilisez simplement l'instruction RETURN. Attraper des exceptions seulement quand cela a du sens.

1

Il y a une erreur dans votre code: old_emp.num_dep = NULL ne peut pas fonctionner, c'est toujours faux. Supposons que cela aurait été old_emp.num_dep IS NULL, alors je pense que votre code ne fonctionnerait pas selon votre intenion. L'exception serait levée en ignorant INSERT INTO EMP2.

Si c'était mon code, et la logique est telle que vous pouvez décider que ce n'est pas une erreur réelle à insérer dans EMP2 dans le cas où le département est manquant, je ne lèverais pas une exception. Vous ne perdez pas non plus cette information, comme vous pouvez toujours voir qu'il y avait des départements manquants (à savoir pour chaque emp avec 0 comme département)

BTW Y at-il une raison particulière d'utiliser 0 pour le département? Pourquoi ne pas simplement utiliser NULL? Apparemment, vous avez décidé qu'il est acceptable d'avoir des employés sans ministère, NULL est une représentation équitable de cela.

Si vous insistez qu'il est en fait une erreur pour l'emp manquer un département, mais se sentent encore il est correct d'insérer le PEM de toute façon, je considère l'écriture comme ceci:

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; 

I Je vous invite cependant à ajouter quelques commentaires dans le code, car si je trouvais un code comme celui que j'ai écrit plus haut, je suspecterais probablement un bug.

3

Je veux faire INSERT INTO EMP2 VALEURS (old_emp.bi, old_emp.nome, old_emp.morada, old_emp.data_entrada, old_emp.data_saida, new_ndep); même après la levée dep_inexistente

L'astuce consiste à soulever cette exception après faire les inserts. Exceptions levées sont effectivement GOTO instructions - le flux de zips de contrôle directement au bloc EXCEPTIONS. Dans la réécriture suivante, j'ai utilisé votre paramètre de new_dep comme un signal pour élever l'exception. Vous pouvez être conscient d'une autre logique métier qui invalide cette approche (c'est-à-dire qu'il existe une raison légitime pour laquelle un enregistrement aurait un zéro de département). Dans ce cas, vous devrez définir un drapeau à la place.

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; 

Trois choses au sujet de votre approche générale:

  1. Toute exception sera shortCircuit la boucle. Aucune ligne supplémentaire ne sera traitée.
  2. Étant donné que vous vous engagez dans LOOP, il peut être difficile de réexécuter le programme, car vous ne pourrez pas reprendre facilement à partir de l'endroit où vous vous étiez arrêté.
  3. La validation à l'intérieur de la boucle peut créer des problèmes avec les erreurs ORA-1555 ou ORA-1002, en particulier s'il s'agit d'une requête de longue durée.

modifier

En fait, votre code soulève beaucoup de questions relatives à la logique procédurale. Bien plus que je ne voudrais aller ici. Les trois que j'ai énumérés ci-dessus sont les questions générales de «meilleures pratiques» mais la logique détaillée du flux conditionnel semble incertaine. Mais je ne connais pas les règles métier que vous implémentez.

0

Donc, si je garde des exceptions serait comme ceci:

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; 

ou je pouvais faire ce que l'on dit, sans lever des exceptions; mais je dois toujours utiliser un curseur.

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; 

Comment voulez-vous le réécrire alors, donc il ne semble pas si "louche"? C'est un peu désordonné, je dois l'admettre. Quoi qu'il en soit, au moins vous m'avez donné des idées très pratiques. Je voudrais voir de meilleures approches, si vous voulez, je suis intéressé.