Pourquoi ne reçois-je PAS une erreur de table de mutation dans le déclencheur?

11

Il est (ou du moins était) connu que vous ne pouvez pas utiliser d'instructions DML sur une table de mutation à l'intérieur d'un déclencheur. Un extrait de la documentation Oracle :

Une table en mutation est une table en cours de modification par une instruction UPDATE, DELETE ou INSERT, ou une table qui peut être mise à jour par les effets d'une contrainte DELETE CASCADE.

La session qui a émis l'instruction de déclenchement ne peut pas interroger ou modifier une table en mutation. Cette restriction empêche un déclencheur de voir un ensemble de données incohérent.

Cependant, je ne comprends pas pourquoi ce déclencheur de démonstration n'échoue pas avec une erreur de "table de mutation" lorsque j'effectue une insert into emputilisation de SQL Developer ou 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;

L'insertion se termine avec succès avec la idvaleur suivante et met à jour tous les empenregistrements. J'utilise Oracle Database 11g Enterprise Edition version 11.2.0.1.0. J'ai lu des informations sur les déclencheurs composés, mais l'échantillon ne les utilise pas.

Centurion
la source
1
Sans rapport avec votre question, mais: ne l' utilisez PASselect max(id) pour attribuer des numéros uniques. Mais ne le fais pas. C'est tout simplement incorrect et il ne s'agrandira pas aussi bien.
a_horse_with_no_name
Oui, je le sais :) L'exemple n'est probablement pas très bon dans ce cas ... Les valeurs d'auto-incrémentation devraient être définitivement implémentées en utilisant des séquences et des déclencheurs.
Centurion
C'est vraiment étrange. BTW: voici un exemple SQLFiddle sqlfiddle.com/#!4/9e59f/2
a_horse_with_no_name
Merci d'avoir partagé des informations, lien sympa. Je ne savais pas qu'il y avait un tel site de test Oracle SQL :)
Centurion
a_horse_with_no_name: Un autre exemple: Fiddle-test-2 (SET salaire = salaire + 10)
ypercubeᵀᴹ

Réponses:

12

Il y a une exception. Lorsque vous définissez un before insertdéclencheur de niveau ligne sur une table et émettez une INSERTinstruction de ligne unique , l' table is mutatingerreur ne sera pas déclenchée . Mais si vous définissez le même type de déclencheur et émettez une INSERTinstruction à plusieurs lignes , l'erreur sera déclenchée . Voici un exemple:

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

Voici une insertinstruction à une seule ligne , qui ne déclenchera pas d'erreur de table de mutation:

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

Voici une instruction d'insertion à plusieurs lignes, qui générera une erreur de table de mutation:

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
la source
Cela semble être le coupable. Avez-vous une référence dans le manuel pour ce comportement?
a_horse_with_no_name
@a_horse_with_no_name si vous avez accès à support.oracle.com, recherchez ID 132569.1( ORA-4091 on BEFORE ROW TRIGGER with INSERT .. into SELECT statement).
Nicholas Krasnov
2
Merci. Assez intéressant, cette exception ne semble être documentée que dans les manuels 8i (!): Docs.oracle.com/cd/F49540_01/DOC/server.815/a68003/… (la section " " Tables de mutation et de contrainte ") que je peux ' t trouver cette déclaration dans les manuels actuels.
a_horse_with_no_name