Почему я не получаю ошибку таблицы мутаций в триггере?

11

Известно (или, по крайней мере, было известно), что вы не можете использовать операторы DML для мутирующей таблицы внутри триггера. Выдержка из документации Oracle :

Мутирующая таблица - это таблица, которая изменяется с помощью оператора UPDATE, DELETE или INSERT, или таблица, которая может обновляться в результате ограничения DELETE CASCADE.

Сеанс, который выдал инициирующий оператор, не может запрашивать или изменять изменяющуюся таблицу. Это ограничение не позволяет триггеру видеть несогласованный набор данных.

Однако я не могу понять, почему этот демонстрационный триггер не завершается с ошибкой «таблица мутаций», когда я insert into empиспользую SQL Developer или SQL * Plus:

CREATE OR REPLACE TRIGGER emp_bri   
  BEFORE INSERT ON emp 
    FOR EACH ROW
BEGIN

  SELECT max(id) + 1 INTO :NEW.id FROM emp;
  UPDATE emp SET salary = 5000;

END emp_bri;

Вставка успешно завершается со следующим idзначением и обновляет все empзаписи. Я использую Oracle Database 11g Enterprise Edition Release 11.2.0.1.0. Я читал о сложных триггерах, но образец их не использует.

центурион
источник
1
Не имеет отношения к вашему вопросу, но: НЕ используйте select max(id)для назначения уникальных номеров. Просто не надо. Это просто неправильно и не будет масштабироваться.
a_horse_with_no_name
Да, я знаю это :) Пример, вероятно, не очень хорош в этом случае ... Значения автоинкремента должны определенно реализовываться с использованием последовательностей и триггеров.
Центурион
Это конечно странно. Кстати: вот пример SQLFiddle sqlfiddle.com/#!4/9e59f/2
a_horse_with_no_name
Спасибо за обмен информацией, классная ссылка. Не знал, что есть такой веб-сайт для тестирования Oracle SQL :)
Centurion
a_horse_with_no_name: Другой пример: Fiddle-test-2 (SET заработная плата = зарплата + 10)
ypercubeᵀᴹ

Ответы:

12

Есть исключение. Когда вы определяете before insertтриггер на уровне строк для таблицы и запускаете одну строку INSERT, table is mutatingошибка не возникает. Но если вы определяете тот же тип триггера и запускаете многострочную INSERTинструкцию, ошибка будет возникать. Вот пример:

SQL> create table TB_TR_TEST(
  2    col1 number,
  3    col2 number
  4  )
  5  ;

Table created

SQL> create or replace trigger TR_TB_TR_TEST
  2  before insert on TB_TR_TEST
  3  for each row
  4  begin
  5    SELECT max(col1) + 1 INTO :NEW.col1
  6      FROM TB_TR_TEST;
  7    UPDATE TB_TR_TEST SET col2 = 5000;
  8  end;
  9  /

Trigger created

Вот insertинструкция, состоящая из одной строки , которая не вызывает ошибку изменяющейся таблицы:

SQL> insert into TB_TR_TEST(col1, col2) values(1,2);

1 row inserted

SQL> insert into TB_TR_TEST(col1, col2) values(3,5);

1 row inserted

SQL> commit;

Commit complete

Вот многострочный оператор вставки, который вызовет ошибку изменяющейся таблицы:

SQL> insert into TB_TR_TEST(col1, col2)
  2    select 1, 2
  3      from dual;

insert into TB_TR_TEST(col1, col2)
  select 1, 2
    from dual

ORA-04091: table HR.TB_TR_TEST is mutating, trigger/function may not see it
ORA-06512: at "HR.TR_TB_TR_TEST", line 2
ORA-04088: error during execution of trigger 'HR.TR_TB_TR_TEST'
Николай Краснов
источник
Это кажется виновником. У вас есть ссылка в руководстве на это поведение?
a_horse_with_no_name
@a_horse_with_no_name, если у вас есть доступ к support.oracle.com, выполните поиск ID 132569.1( ORA-4091 on BEFORE ROW TRIGGER with INSERT .. into SELECT statement).
Николай Краснов
2
Спасибо. Интересно, что это исключение, по-видимому, задокументировано только в руководствах 8i (!): Docs.oracle.com/cd/F49540_01/DOC/server.815/a68003/… (раздел «Мутирующие и ограничивающие таблицы »), я могу ' не найти это утверждение в текущих руководствах.
a_horse_with_no_name