quarta-feira, 3 de agosto de 2011

Definindo a ordem de execução de triggers no Oracle 11g ...

Olá,

Até o Oracle 11g, não se podia determinar a ordem de execução de triggers do mesmo tipo para uma tabela. Por exemplo, se temos 3 triggers em uma tabela na qual todas são BEFORE INSERT, qual será a ordem de execução das mesmas? A resposta é: impossível determinar, pois se você tiver várias triggers do mesmo tipo na tabela, então o Oracle escolherá uma ordem arbitrária e randômica para executá-las. Então dependendo da regra de negócio existente, isso poderia ser um problema ou não, pois a solução seria criar uma única trigger do tipo em questão e colocar toda a regra de negócio (PL/SQL) nela. A desvantagem nesse caso seria apenas de cunho administrativo e de manutenção, pois a trigger poderia ficar imensa, complexa e de difícil compreensão.

Na minha visão, a vantagem de se ter mais de uma trigger seria a de trazer mais praticidade e modularidade de forma a separar a regra de negócio de acordo com o objetivo de cada uma, além de trazer uma certa flexibilidade na hora realizar manutenções no código. Imagina um cenário onde existe uma única trigger que, além de realizar grande parte da regra de negócio, ainda realiza operações para geração de logs de transação. Veja a flexibilidade se criássemos 2 triggers: uma para a regra de negócio em si e outra para geração de logs de transação. Alguém um dia poderia sugerir: "A rotina está muito lenta hoje, acho melhor desabilitarmos a trigger que realiza a geração dos logs de transação" Fácil não?


No mais, neste artigo irei demonstrar que com o Oracle 11g podemos determinar a ordem de execução das triggers de uma tabela que são de um mesmo tipo. Na criação de uma trigger no Oracle 11g, existe uma nova cláusula chamada "follows" que nos permitirá definir a ordem de execução das mesmas.

No primeiro exemplo abaixo, irei criar 3 triggers do tipo BEFORE INSERT sem definir uma ordem de execução.

C:\>sqlplus scott/tiger

SQL*Plus: Release 11.2.0.1.0 Production on Qua Ago 3 21:34:36 2011

Copyright (c) 1982, 2010, Oracle. All rights reserved.

Conectado a:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> create table t1 (id number);

Tabela criada.

SQL> create or replace trigger trg_teste_01
2 before insert on t1
3 for each row
4 begin
5 dbms_output.put_line('trigger trg_teste_01');
6 end;
7 /

Gatilho criado.

SQL> create or replace trigger trg_teste_02
2 before insert on t1
3 for each row
4 begin
5 dbms_output.put_line('trigger trg_teste_02');
6 end;
7 /

Gatilho criado.

SQL> create or replace trigger trg_teste_03
2 before insert on t1
3 for each row
4 begin
5 dbms_output.put_line('trigger trg_teste_03');
6 end;
7 /

Gatilho criado.

Após a criação da tabela e das triggers, irei inserir um registro na tabela T1.

SQL> set serveroutput on
SQL> insert into t1 values (1);
trigger trg_teste_03
trigger trg_teste_02
trigger trg_teste_01

1 linha criada.

Perceberam que a trigger trg_teste_03 foi acionada primeiro? Quem garante que a mesma será executada primeiro na próxima vez? Bom, no segundo exemplo abaixo irei criar novamente as triggers só que, agora, definindo a ordem de execução: trg_teste_01, depois trg_teste_02 e depois trg_teste_03.

SQL> create or replace trigger trg_teste_01
2 before insert on t1
3 for each row
4 begin
5 dbms_output.put_line('trigger trg_teste_01');
6 end;
7 /

Gatilho criado.

SQL> create or replace trigger trg_teste_02
2 before insert on t1
3 for each row
4 follows trg_teste_01
5 begin
6 dbms_output.put_line('trigger trg_teste_02');
7 end;
8 /

Gatilho criado.

SQL> create or replace trigger trg_teste_03
2 before insert on t1
3 for each row
4 follows trg_teste_02
5 begin
6 dbms_output.put_line('trigger trg_teste_03');
7 end;
8 /

Gatilho criado.

SQL> insert into t1 values (2);
trigger trg_teste_01
trigger trg_teste_02
trigger trg_teste_03

1 linha criada.

Pronto. Podemos ver que as mesmas foram acionadas de acordo com a ordem que eu havia definido anteriormente. Poderíamos inclusive desabilitar uma das triggers que não haveria problema nenhum:
SQL> alter trigger trg_teste_02 disable;

Gatilho alterado.

SQL> insert into t1 values (3);
trigger trg_teste_01
trigger trg_teste_03

1 linha criada.

Agora, caso alguma trigger que é dependente de outra for dropada, aí sim teremos problemas pois não conseguiremos realizar algumas operações até que o problema seja sanado.

SQL> drop trigger trg_teste_02;

Gatilho eliminado.

SQL> select object_name,status from user_objects where object_type='TRIGGER';

OBJECT_NAME STATUS
------------------------------ -------
TRG_TESTE_01 VALID
TRG_TESTE_03 INVALID


SQL> insert into t1 values (3);
insert into t1 values (3)
*
ERRO na linha 1:
ORA-04045: erros durante a recompilação/revalidação de SCOTT.TRG_TESTE_03
ORA-04098: gatilho 'SCOTT.TRG_TESTE_03' é inválido e a revalidação falhou

SQL> drop table t1;
drop table t1
*
ERRO na linha 1:
ORA-00604: ocorreu um erro no nível 1 SQL recursivo
ORA-04045: erros durante a recompilação/revalidação de SCOTT.TRG_TESTE_03
ORA-04098: gatilho 'SCOTT.TRG_TESTE_03' é inválido e a revalidação falhou

SQL> alter trigger trg_teste_03 disable;

Gatilho alterado.

SQL> insert into t1 values (4);
trigger trg_teste_01

1 linha criada.

SQL> drop table t1;

Tabela eliminada.