Dlaczego NIE dostaję błędu tabeli mutacji w wyzwalaczu?

11

Wiadomo (lub przynajmniej wiadomo), że nie można używać instrukcji DML w tabeli mutacji wewnątrz wyzwalacza. Fragment dokumentacji Oracle :

Tabela mutacji to tabela, która jest modyfikowana za pomocą instrukcji UPDATE, DELETE lub INSERT, lub tabela, która może być aktualizowana przez skutki ograniczenia DELETE CASCADE.

Sesja, która wydała instrukcję wyzwalającą, nie może zapytać ani zmodyfikować tabeli mutacji. To ograniczenie zapobiega wyświetlaniu przez wyzwalacz niespójnego zestawu danych.

Nie mogę jednak zrozumieć, dlaczego ten wyzwalacz demonstracyjny nie działa z błędem „mutating table”, gdy wykonuję program insert into empSQL Developer lub 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;

Wstawianie kończy się pomyślnie kolejną idwartością i aktualizuje wszystkie emprekordy. Korzystam z Oracle Database 11g Enterprise Edition Release 11.2.0.1.0. Czytałem o wyzwalaczach złożonych, ale próbka ich nie używa.

Centurion
źródło
1
Nie ma związku z twoim pytaniem, ale: NIE używaj select max(id)do przypisywania unikalnych numerów. Po prostu nie. Jest to po prostu niepoprawne i nie będzie również skalowane.
a_horse_w_no_name
Tak, wiem to :) Przykład prawdopodobnie nie jest zbyt dobry w tym przypadku ... Wartości autoinkrementacji powinny być zdecydowanie zaimplementowane przy użyciu sekwencji i wyzwalaczy.
Centurion
To z pewnością dziwne. Btw: tutaj jest SQLFiddle przykład sqlfiddle.com/#!4/9e59f/2
a_horse_with_no_name
Dziękujemy za udostępnienie informacji, fajny link. Nie wiedziałem, że jest taka witryna testowa Oracle SQL :)
Centurion
a_horse_w_no_name: Kolejny przykład: Fiddle-test-2 (SET pensja = pensja + 10)
ypercubeᵀᴹ

Odpowiedzi:

12

Jest wyjątek. Po zdefiniowaniu before insertwyzwalacza na poziomie wiersza w tabeli i wydaniu INSERTinstrukcji z jednym wierszem table is mutatingbłąd nie zostanie zgłoszony. Ale jeśli zdefiniujesz ten sam rodzaj wyzwalacza i wydasz INSERTinstrukcję zawierającą wiele wierszy , błąd zostanie zgłoszony. Oto przykład:

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

Oto insertinstrukcja jednorzędowa , która nie podniesie błędu tabeli mutacji:

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

Oto instrukcja wstawiania zawierająca wiele wierszy, która podniesie błąd tabeli mutacji:

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'
Mikołaj Krasnow
źródło
To wydaje się być sprawcą. Czy masz odniesienie w tym podręczniku?
a_horse_w_no_name
@ koń_nazwa_nazwa, jeśli masz dostęp do support.oracle.com, wyszukaj ID 132569.1( ORA-4091 on BEFORE ROW TRIGGER with INSERT .. into SELECT statement).
Mikołaj Krasnow
2
Dzięki. Co ciekawe, ten wyjątek wydaje się udokumentowany tylko w podręcznikach 8i (!): Docs.oracle.com/cd/F49540_01/DOC/server.815/a68003/… (sekcja „ Tabele mutacji i ograniczania ”) mogę ” znajdź to stwierdzenie w aktualnych instrukcjach
a_horse_w_na_nazwa