вопрос об исключении pl / sql - PullRequest
2 голосов
/ 18 ноября 2009

следующий текст является выдержкой из документации оракула Справочник по языку Oracle® Database PL / SQL 11g, выпуск 1 (11.1) :

Необработанные исключения также могут влиять Подпрограммы. Если вы выходите из подпрограммы успешно, PL / SQL присваивает значения Вне параметров. Однако, если вы выходите с необработанным исключением, PL / SQL не присваивает значения OUT параметры (если они не NOPOPY параметры). Также, если хранится Сбой подпрограммы с необработанным исключение, PL / SQL не откатывается работа с базой данных, выполненная подпрограммой.

обратите внимание на жирный текст, это правда? мне это интересно, поэтому я написал следующий пример, чтобы проверить это.

-- create a test table
CREATE TABLE e AS SELECT * FROM HR.EMPLOYEES;   

-- create p1 which will update a row in table e
CREATE OR REPLACE PROCEDURE p1
IS
    ex EXCEPTION;
    row e%ROWTYPE;
BEGIN
    select * into row from e where employee_id=100;

    row.employee_id := 100;
    row.first_name := 'yang';

    -- update
    UPDATE e set ROW = row where employee_id = 100;
    -- and raise an error
    RAISE ex;
END;


BEGIN
    -- call the upper procedure
    p1;
END;

-- test whether update success
select * from e where employee_id=100;

-- the upper query gives me
Steven

поэтому мой вопрос: я прав?

Ответы [ 2 ]

9 голосов
/ 18 ноября 2009

См. Этот вопрос по SO: Откатывает ли Oracle транзакцию при ошибке?

В вашем случае процедура P1 будет либо успешной, либо неудачной и откатит свои изменения. Почему это выглядит так, будто утверждение из документации утверждает обратное (p1 не выполняется в середине процедуры и оставляет незавершенную работу)?

Ответ лежит в предложении непосредственно перед вашей цитатой :

Помните, что если не удается найти обработчик для возбужденного исключения, PL / SQL возвращает необработанную ошибку исключения в среду хоста, которая определяет результат. Например, в среде прекомпиляторов Oracle любые изменения в базе данных, сделанные с помощью оператора SQL или блока PL / SQL, откатываются.

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

Мы можем показать это поведение, поместив блок WHEN OTHERS (а не повторно вызывая исключение - конечно, это действительно очень плохая идея см. Ниже почему) в вашем примере:

SQL> BEGIN
  2     -- call the upper procedure
  3     p1;
  4  EXCEPTION
  5     WHEN OTHERS THEN
  6        dbms_output.put_line('log error...');
  7  END;
  8  /

log error...

PL/SQL procedure successfully completed

SQL> select employee_id, first_name from e where employee_id = 100;

EMPLOYEE_ID FIRST_NAME
----------- --------------------
        100 yang

Вы действительно никогда не хотите, чтобы сделал это : мы оставили незавершенную работу, ошибка регистрируется, и, не поднимая ее повторно, мы имеем потенциально серьезные ошибка. Кроме того, молчаливое игнорирование исключений - это рецепт бедствий.

2 голосов
/ 19 ноября 2009

"Кроме того, если хранимая подпрограмма дает сбой с необработанным исключением, PL / SQL не откатывает работу базы данных, выполненную подпрограммой."

Приведенная выше цитата конкретно относится к хранимым подпрограммам, но следующий фрагмент кода является анонимным блоком, а не хранимой подпрограммой

BEGIN
    -- call the upper procedure
    p1;
END;

Цитата как таковая не применяется. Отказ анонимного блока верхнего уровня - это тот, который выполняет откат (как и любой другой оператор SQL) Тестирование с использованием следующего кода показывает, что к тому времени, когда срабатывает триггер SERVERERROR (т. Е. ДО возврата к хосту), значения INSERT значений 1 и 10 уже откатаны (поскольку повторная вставка 1 не приводит к сбою). на дубликат ключа или тупик).

drop table test_se_auto_tbl;

create table test_se_auto_tbl (id number(2) primary key, val varchar2(20));

create or replace trigger test_se_auto_trg after servererror on schema 
begin
  for c_rec in (select id, val from test_se_auto_tbl) loop
dbms_output.put_line(c_rec.id||':'||c_rec.val);
  end loop;
  dbms_output.put_line('Trigger');
  insert into test_se_auto_tbl values (1,'test ');
end;
/

begin
  insert into test_se_auto_tbl values (1,'test ');
  insert into test_se_auto_tbl values (10,'test 10');
  insert into test_se_auto_tbl values (100,'test 100');
end;
/

select id, val from test_se_auto_tbl;

Еще один сценарий, подтверждающий эту гипотезу. В этом случае анонимный блок PL / SQL вызывается в EXECUTE IMMEDIATE, вложенном в другой блок PL / SQL. Несмотря на то, что исключение перехвачено внешним блоком, вставка уже откатана, поскольку EXECUTE IMMEDIATE выполняет атомарный оператор.

DECLARE
  v_num NUMBER;
begin
   begin
     execute immediate 
         'declare 
           v_num number(2); 
         begin 
           insert into dummy values (1);
           dbms_output.put_line(101);
           v_num := 100;
         end;';
   exception
      when others then null;
   end;
   select count(*) into v_num from dummy;
   dbms_output.put_line(v_num);
end;
/ 
...