sql >> Base de Datos >  >> RDS >> Oracle

Oracle como solución alternativa a las tablas mutantes

El error del disparador de mutación de Oracle ocurre cuando un disparador hace referencia a la tabla que posee el disparador, lo que resulta en el mensaje "ORA-04091:el nombre de la tabla está mutando, es posible que el disparador/la función no lo vean".

Echemos un vistazo a las soluciones alternativas existentes.

El primero, a través del paquete, es antiguo y parece ser efectivo, sin embargo, lleva mucho tiempo prepararlo y ejecutarlo. El segundo es simple y se realiza con disparadores compuestos.

create table turtles 
as
select 'Splinter' name, 'Rat' essence from dual union all
select 'Leonardo', 'Painter' from dual union all
select 'Rafael', 'Painter' from dual union all
select 'Michelangelo', 'Painter'  from dual union all
select 'Donatello', 'Painter'  from dual;

Cuando Splinter mute de rata a sensei, los pintores tendrán que convertirse automáticamente en ninjas. Este activador parece adecuado:

create or replace trigger tr_turtles_bue
before update of essence
on turtles
for each row
when (
  new.name = 'Splinter' and old.essence = 'Rat' and new.essence = 'Sensei'
)
begin
  update turtles
     set essence = 'Ninja'
   where essence = 'Painter';  
end;

Sin embargo, al actualizar el registro:

update turtles
   set essence = 'Sensei'
 where name = 'Splinter'

Se produce el siguiente error:

ORA-04091:la tabla SCOTT.TURTLES está mutando, es posible que el activador o la función no lo vean

Eliminemos este disparador:

drop trigger tr_turtles_bue;

El método 1: Usando el paquete y el activador de nivel de instrucción.

create or replace package pkg_around_mutation 
is
  bUpdPainters boolean;
  procedure update_painters;  
end pkg_around_mutation;
/

create or replace package body pkg_around_mutation
is
  procedure update_painters
  is
  begin   
    if bUpdPainters then
      bUpdPainters := false;
      update turtles
         set essence = 'Ninja'
       where essence = 'Painter';
    end if;
  end;  
end pkg_around_mutation;
/

create or replace trigger tr_turtles_bue
before update of essence
on turtles
for each row
when (
  new.name = 'Splinter' and old.essence = 'Rat' and new.essence = 'Sensei' 
)
begin
  pkg_around_mutation.bUpdPainters := true;  
end tr_turtles_bue; 
/

create or replace trigger tr_turtles_bu
after update
on turtles
begin
  pkg_around_mutation.update_painters;  
end tr_turtles_bu;
/

El método 2: Uso de activadores DML compuestos (disponibles a partir de Oracle 11g).

create or replace trigger tr_turtles_ue
  for update of essence
  on turtles
  compound trigger
    bUpdPainters  boolean;
 
  before each row is
  begin
    if :new.name = 'Splinter' and :old.essence = 'Rat' and :new.essence = 'Sensei' then
      bUpdPainters := true;
    end if;
  end before each row;
  
  after statement is
  begin
    if bUpdPainters then
      update Turtles
         set essence = 'Ninja'
       where essence = 'Painter';
    end if;
  end after statement;
end tr_turtles_ue;

Intentemos lo siguiente:

update turtles
   set essence = 'Sensei'
 where name = 'Splinter'

Incluso si se enfrenta a un caso de mutación más complejo, puede utilizar la idea mencionada anteriormente como solución alternativa. En el disparador de nivel de instrucción, a diferencia del disparador de nivel de fila, no se produce ninguna mutación. Puede usar variables (etiquetas, pestillos, tablas PL SQL) en un paquete adicional o variables que son globales para todas las secciones del activador compuesto, lo cual es preferible a partir de la versión Oracle 11g. Entonces, ahora también sabes kung fu.

Puede encontrar información adicional sobre activadores en:Activadores DML compuestos

Siéntase libre de agregar cualquier comentario.