Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Oracle como solução alternativa de tabelas mutantes


O erro do gatilho de mutação do Oracle ocorre quando um gatilho faz referência à tabela que possui o gatilho, resultando na mensagem “ORA-04091:nome da tabela está mudando, gatilho/função pode não vê-lo”.

Vamos dar uma olhada nas soluções alternativas existentes.

O primeiro, através do pacote, é antigo e parece ser eficaz, porém, leva muito tempo para prepará-lo e executá-lo. O segundo é simples e executado usando gatilhos compostos.


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;



Quando Splinter se transforma de rato em sensei, os pintores terão que se transformar automaticamente em ninjas. Este gatilho parece ser adequado:
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;

No entanto, ao atualizar o registro:
update turtles
   set essence = 'Sensei'
 where name = 'Splinter'

Ocorre o seguinte erro:

ORA-04091:a tabela SCOTT.TURTLES está em mutação, o gatilho/função pode não vê-la

Vamos excluir este gatilho:
drop trigger tr_turtles_bue;

O método 1: Usando o pacote e o gatilho de nível de instrução.
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;
/

O método 2: Usando gatilhos DML compostos (disponíveis a partir do 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;

Vamos tentar o seguinte:
update turtles
   set essence = 'Sensei'
 where name = 'Splinter'



Mesmo que você tenha enfrentado um caso mais complexo de mutação, você pode usar a ideia acima mencionada como uma solução alternativa. No trigger de nível de instrução, diferentemente do trigger de nível de linha, nenhuma mutação ocorre. Você pode usar variáveis ​​(tags, latches, tabelas PL SQL) em um pacote adicional ou variáveis ​​que são globais para todas as seções do trigger composto, o que é preferível a partir da versão Oracle 11g. Então, agora você também conhece o kung fu.

Você pode encontrar informações adicionais sobre gatilhos em:Compound DML Triggers

Sinta-se à vontade para adicionar quaisquer comentários.