¿Por qué NO obtengo un error de tabla de mutaciones en el activador?

11

Se sabe (o al menos se sabía) que no se pueden usar sentencias DML en una tabla de mutaciones dentro de un activador. Un extracto de la documentación de Oracle :

Una tabla de mutación es una tabla que está siendo modificada por una instrucción UPDATE, DELETE o INSERT, o una tabla que podría actualizarse por los efectos de una restricción DELETE CASCADE.

La sesión que emitió la declaración de activación no puede consultar ni modificar una tabla de mutaciones. Esta restricción evita que un disparador vea un conjunto de datos inconsistente.

Sin embargo, no puedo entender por qué este desencadenador de demostración no falla con un error de "tabla de mutaciones" cuando realizo un insert into empuso de SQL Developer o 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;

La inserción se completa con éxito con el siguiente idvalor y actualiza todos los empregistros. Estoy usando Oracle Database 11g Enterprise Edition Release 11.2.0.1.0. He leído sobre los desencadenantes compuestos, pero la muestra no los usa.

Centurion
fuente
1
Sin relación con su pregunta, pero: NO lo use select max(id)para asignar números únicos. Solo no lo hagas. Es simplemente incorrecto y no escalará también.
a_horse_with_no_name
Sí, lo sé :) El ejemplo probablemente no sea muy bueno en este caso ... Los valores de autoincremento definitivamente deben implementarse usando secuencias y disparadores.
Centurión el
Esto seguro es extraño. Por cierto: aquí hay un ejemplo de SQLFiddle sqlfiddle.com/#!4/9e59f/2
a_horse_with_no_name
Gracias por compartir información, enlace genial. No sabía que existía ese sitio web de prueba de Oracle SQL :)
Centurion
a_horse_with_no_name: Otro ejemplo: Fiddle-test-2 (SET salario = salario + 10)
ypercubeᵀᴹ

Respuestas:

12

Hay una excepción Cuando define un before insertdesencadenador de nivel de fila en una tabla y emite una INSERTdeclaración de fila única , el table is mutatingerror no se generará. Pero si define el mismo tipo de disparador y emite una INSERTinstrucción de varias filas , se generará el error. Aquí hay un ejemplo:

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

Aquí hay una insertdeclaración de una sola fila , que no generará un error de tabla mutante:

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

Aquí hay una instrucción de inserción de varias filas, que generará un error de tabla mutante:

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
fuente
Eso parece ser el culpable. ¿Tiene una referencia en el manual para este comportamiento?
a_horse_with_no_name
@a_horse_with_no_name si tiene acceso a support.oracle.com, busque ID 132569.1( ORA-4091 on BEFORE ROW TRIGGER with INSERT .. into SELECT statement).
Nicholas Krasnov el
2
Gracias. Es bastante interesante que esta excepción solo esté documentada en los manuales 8i (!): Docs.oracle.com/cd/F49540_01/DOC/server.815/a68003/… (la sección " " Tablas de mutación y restricción ") puedo" t encontrar esa declaración en los manuales actuales.
a_horse_with_no_name