область действия исключения, обработка ваших собственных исключений в коде PLSQL - PullRequest
1 голос
/ 22 января 2010

У меня есть эта процедура:

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, но после прочтения ссылки оракула, я немного запутался; По сути, когда оно пустое, я не хочу делать эту вставку, в противном случае я хочу вставить, даже если номер отдела равен нулю (который я перехожу к 0).

Итак, правильно ли работает код или как мне повысить мои исключения или обработать заранее определенные исключения для моего случая?

Ответы [ 4 ]

3 голосов
/ 22 января 2010

Я хочу сделать 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 утверждениями - поток управления проникает прямо в блок ИСКЛЮЧЕНИЯ. В следующем переписывании я использовал вашу настройку 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 голосов
/ 22 января 2010

Я не думаю, что исключения должны использоваться как неэлегантное утверждение GOTO. Если вы хотите структурировать свой код, вы можете использовать процедуры (и подпроцедуры). Если работа выполняется в одном месте кода, просто используйте оператор RETURN. Поймать исключения только тогда, когда это имеет смысл.

1 голос
/ 22 января 2010

В вашем коде есть ошибка: old_emp.num_dep = NULL не может работать, всегда ложно.

Предположим, это было бы old_emp.num_dep IS NULL, тогда я думаю, что ваш код не будет работать в соответствии с вашим намерением. Исключение будет возникать в обход INSERT INTO EMP2.

Если бы это был мой код, и логика такова, что вы можете решить, что вставка в EMP2 не является реальной ошибкой в ​​случае отсутствия отдела, я бы не стал выдвигать исключение. Вы также не теряете эту информацию, поскольку вы всегда можете видеть, что отсутствовали отделы (а именно для каждого emp с 0 в качестве отдела)

Кстати, есть ли особая причина для использования 0 для отдела? Почему бы просто не использовать NULL? Судя по всему, вы решили, что иметь сотрудников без отдела вполне нормально, NULL - это справедливое представление.

Если вы настаиваете, что 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;

Я призываю вас добавить некоторые комментарии в коде, потому что, если бы я нашел код, подобный тому, что я написал выше, я, вероятно, заподозрил бы ошибку.

0 голосов
/ 22 января 2010

Итак, если я сохраню исключения, это будет выглядеть так:

    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;

Как бы вы переписали его тогда, чтобы он не выглядел так "ненадежно"? Это немного грязно, я должен признать. Во всяком случае, по крайней мере, вы дали мне очень практическое понимание. Я хотел бы увидеть некоторые лучшие подходы, если вы хотите, мне интересно.

...