Warum wird beim Trigger KEIN mutierender Tabellenfehler angezeigt?

11

Es ist (oder war zumindest bekannt), dass Sie keine DML-Anweisungen für eine mutierende Tabelle in einem Trigger verwenden können. Ein Auszug aus der Oracle-Dokumentation :

Eine mutierende Tabelle ist eine Tabelle, die durch eine UPDATE-, DELETE- oder INSERT-Anweisung geändert wird, oder eine Tabelle, die möglicherweise durch die Auswirkungen einer DELETE CASCADE-Einschränkung aktualisiert wird.

Die Sitzung, die die auslösende Anweisung ausgegeben hat, kann keine mutierende Tabelle abfragen oder ändern. Diese Einschränkung verhindert, dass ein Trigger einen inkonsistenten Datensatz sieht.

Ich kann jedoch nicht verstehen, warum dieser Demo-Trigger nicht mit einem "Mutating Table" -Fehler fehlschlägt, wenn ich insert into empSQL Developer oder SQL * Plus verwende:

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;

Das Einfügen wird mit dem nächsten idWert erfolgreich abgeschlossen und aktualisiert alle empDatensätze. Ich verwende Oracle Database 11g Enterprise Edition Release 11.2.0.1.0. Ich habe über zusammengesetzte Trigger gelesen, aber das Beispiel verwendet sie nicht.

Zenturio
quelle
1
Nicht mit Ihrer Frage verbunden, aber: NICHT verwenden select max(id), um eindeutige Nummern zuzuweisen. Tu es einfach nicht. Es ist einfach falsch und wird nicht so gut skaliert.
a_horse_with_no_name
Ja, das weiß ich :) Das Beispiel ist in diesem Fall wahrscheinlich nicht sehr gut ... Autoincrement-Werte sollten definitiv mithilfe von Sequenzen und Triggern implementiert werden.
Centurion
Das ist sicher seltsam. Übrigens: Hier ist ein SQLFiddle-Beispiel sqlfiddle.com/#!4/9e59f/2
a_horse_with_no_name
Vielen Dank für das Teilen von Informationen, cooler Link. Ich wusste nicht, dass es eine solche Oracle SQL-Testwebsite gibt :)
Centurion,
a_horse_with_no_name: Ein weiteres Beispiel: Fiddle-Test-2 (SET Gehalt = Gehalt + 10)
ypercubeᵀᴹ

Antworten:

12

Es gibt eine Ausnahme. Wenn Sie einen before insertTrigger auf Zeilenebene für eine Tabelle definieren und eine einzelne Zeilenanweisung ausgeben INSERT, wird der table is mutatingFehler nicht ausgelöst . Wenn Sie jedoch dieselbe Art von Trigger definieren und eine mehrzeilige INSERTAnweisung ausgeben , wird der Fehler ausgelöst . Hier ist ein Beispiel:

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

Hier ist eine einzeilige insertAnweisung, die keinen mutierenden Tabellenfehler auslöst:

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

Hier ist eine mehrzeilige Einfügeanweisung, die einen mutierenden Tabellenfehler auslöst:

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'
Nicholas Krasnov
quelle
Das scheint der Schuldige zu sein. Haben Sie eine Referenz im Handbuch für dieses Verhalten?
a_horse_with_no_name
@a_horse_with_no_name Wenn Sie Zugriff auf support.oracle.com haben, suchen Sie nach ID 132569.1( ORA-4091 on BEFORE ROW TRIGGER with INSERT .. into SELECT statement).
Nicholas Krasnov
2
Vielen Dank. Interessanterweise scheint diese Ausnahme nur in den 8i (!) - Handbüchern dokumentiert zu sein: docs.oracle.com/cd/F49540_01/DOC/server.815/a68003/… (der Abschnitt "Mutating and Constraining Tables ") kann ich ' t finden Sie diese Aussage in den aktuellen Handbüchern.
a_horse_with_no_name