Você está na página 1de 7

Oracle - Trigger

Publicado em: 10/05/2004

Triggers so procedimentos que podem ser gravados em Java, PL/SQL ou C. So executados (ou disparados) implicitamente quando uma tabela modificada, um objeto criado ou ocorrem algumas aes de usurio ou de sistema de banco de dados. As triggers so similares as stored procedures diferindo, apenas, na maneira como so chamadas. A trigger executada implicitamente quando ocorre algum evento de trigger enquanto a stored procedure deve ser executado explicitamente. Uma trigger composta por quatro partes: Momento Evento Tipo Corpo

O momento define quando uma trigger ir ser acionada. Pode ser: - BEFORE (tabela) - AFTER (tabela) - INSTEAD OF (view) BEFORE indica que os comandos PL/SQL do corpo da trigger sero executados ANTES dos dados da tabela serem alterados. Normalmente usamos BEFORE nos casos em que precisamos incializar variveis globais, validar regras de negcios, alterar o valor de flags ou para salvar o valor de uma coluna antes de alterarmos o valor delas. Exemplo:
CREATE OR REPLACE TRIGGER novo_func BEFORE... . . . END; /

AFTER indica que os comando PL/SQL do corpo da trigger ser executado APS os dados da tabela serem alterados. Normalmente usamos AFTER para completar os dados de outras tabelas e para completar a atividade de outra trigger de momento BEFORE. Exemplo:
CREATE OR REPLACE TRIGGER novo_func AFTER... . . . END; /

INSTEAD OF indica que a trigger ir ser executada no lugar da instruo que disparou a trigger. Literalmente, a instruo substituda pela trigger. Essa tcnica permite que faamos, por exemplo, alteraes em uma tabela atravs de uma view. usado nos casos em que a view no pode alterar uma tabela por no referenciar uma coluna com a constraint not null. Nesse caso a trigger pode atualizar a coluna que a view no tem acesso.

Dois detalhes muito importantes sobre INSTEAD OF: - S funcionam com views e - sempre de linha. Ser considerado assim, mesmo que "FOR EACH ROW" for omitido. Exemplo:
CREATE OR REPLACE TRIGGER novo_func INSTEAD OF INSERT ON vemp FOR EACH ROW WHEN ... . . . END; /

O evento define qual a instruo DML que aciona a trigger. Informa qual instruo SQL ir disparar a trigger. Pode ser: - INSERT - UPDATE - DELETE Quando o evento for um UPDATE podemos informar quais colunas que, ao serem alteradas, iro disparar a trigger. O mesmo NO ocorre com INSERT e DELETE porque essas instrues sempre afetam a linha por inteiro. Exemplo:
CREATE OR REPLACE TRIGGER novo_func AFTER INSERT ON emp . . . END; /

O evento pode conter uma, duas ou todas as trs operaes DML em uma nica linha de comando. Exemplo:
CREATE OR REPLACE TRIGGER novo_func BEFORE INSERT OR UPDATE OR DELETE ON emp . . . END; /

O tipo define quantas vezes uma trigger ser executa. A trigger pode ser executada uma vez para a instruo que a disparou ou ser disparada para cada linha afetada pela instruo que disparou a trigger. Pode ser: - Instruo (STATEMENT) - Linha (ROW) Quando a trigger for do tipo instruo ela ser disparada uma vez para cada evento de trigger, mesmo que nenhuma linha tenha sido afetada. So teis para aquelas trigger que eventualmente no alteram dados ou para situaes onde o que queremos uma resposta da trigger, por exemplo, em uma restrio complexa de negcio. Por DEFAULT toda trigger deste tipo. Exemplo:

CREATE OR REPLACE TRIGGER novo_func BEFORE INSERT OR UPDATE OR DELETE ON emp FOR EACH STATEMENT . . . END; /

Quando a trigger for do tipo linha, a trigger ser executada toda vez que a tabela for afetada pelo evento da trigger. Se nenhuma linha for afetada a trigger no ser executada. So muito teis quando a ao da trigger depende dos dados afetados pelo evento da trigger. Exemplo:
CREATE OR REPLACE TRIGGER novo_func BEFORE INSERT OR UPDATE OR DELETE ON emp FOR EACH ROW . . . END; /

O corpo define a ao que uma trigger ir executar quando acionada. O corpo de uma trigger composto por um bloco PL/SQL, a chamada de uma PROCEDURE ou por um procedimento JAVA. Por definio, o tamanho de uma trigger no pode ultrapassar 32K. Como, normalmente, precisamos trabalhar com os valores antes e depois da alterao dos dados, a trigger permite que faamos referencia aos valores antes da alterao (OLD) e aps a alterao (NEW). O nome de uma trigger deve ser nico dentro de um mesmo esquema, e sua sintaxe bsica :
CREATE [OR REPLACE] TRIGGER [schema.] nome_da_trigger [BEFORE|AFTER] [DELETE|OR INSERT|OR UPDATE[OF coluna]] ON [schema.] nome_da_tabela_ou_da_view [REFERENCING [OLD [AS] OLD] [NEW [AS] NEW] [FOR EACH ROW] [WHEN [condio]] BLOCO PL/SQL

Onde: Nome_da_trigger o nome da trigger; Nome_da_tabela_ou_da_view indica a tabela ou view associada com a trigger; Corpo_da_trigger a ao que a trigger ir executar. Inicia por DECLARE ou BEGIN e termina por END. Tambm pode conter a chamada de um procedimento. O uso do nome da coluna na clusula UPDATE pode aumentar a performance porque a trigger s ser disparada quando aquela coluna especificada na clusula for alterada. Agora que sabemos como criar uma trigger veremos um exemplo completo: Primeiro vamos criar uma tabela para gravar um registro de todos os usurios que se conectaram ao banco:

CREATE TABLE vigia (marca VARCHAR2(100)); CREATE OR REPLACE TRIGGER marca_logon AFTER LOGON ON DATABASE BEGIN INSERT INTO sys.vigia VALUES (USER || ' entrou no sistema em ' || TO_CHAR(sysdate, 'DD-MM-YYYY HH24:MI:SS')); COMMIT; END; /

Pronto, temos nossa primeira trigger. Ela registra o nome do usurio e a que horas ele entrou. Esse exemplo foi retirado diretamente da documentao Oracle. No nosso exemplo fazemos referencia a um evento do sistema ao invs de referenciarmos uma tabela. Outros eventos do sistema so: AFTER SERVERERROR AFTER LOGON BEFORE LOGOFF AFTER STARTUP BEFORE SHUTDOWN

Voc pode criar triggers usando os eventos acima para DATABASE e SCHEMA. As duas excees so SHUTDOWN e STARTUP que s se aplicam a DATABASE. Exemplo:
CREATE OR REPLACE TRIGGER marca_logoff BEFORE LOGOFF ON SCHEMA BEGIN INSERT INTO sys.vigia VALUES (USER || ' saiu do sistema em ' || TO_CHAR(sysdate, 'DD-MM-YYYY HH24:MI:SS')); COMMIT; END; /

Eventualmente podemos ter algum tipo de erro em nossa trigger. Para verificar quais so os erros de compilao que temos na trigger basta usar o comando SHOW ERRORS TRIGGER nome_da_trigger. Caso voc queira ver os erros de compilao da ltima trigger que voc compilou pode escrever apenas SHOW ERRORS ou SHO ERR. Ao executarmos esse comando ele mostrar a linha onde est o erro. Ateno: caso a linha onde est o erro se estenda por mais de uma linha, este comando indicar o incio da linha. Vamos criar uma trigger com erro para servir como exemplo:
CREATE OR REPLACE TRIGGER marca_logon AFTER LOGON ON DATABASE BEGIN INSERT INTO sys.vigia VALUES (USER || ' entrou no sistema em ' || TO_CHAR(sysdate, 'DD-MM-YYYY HH24:MI:SS)); COMMIT; END; /

Gatilho criado com erro de compilao Qual o erro desse gatilho? um erro bem banal, no caso deixamos de fechar a apstrofe (ou aspas simples ou quote) no final da instruo TO_CHAR. Ao executarmos o SHOW ERROR ele ir mostrar que houve um erro na linha 4. Isso

porque ele aponta onde a linha que contem o erro comeou a ser escrita e no a linha onde efetivamente ocorreu o erro est. Caso precise de mais informaes sobre sua trigger, a view USER_TRIGGERS pode fornecer informaes muito teis. Exemplo:
SELECT trigger_name FROM user_triggers;

Com o nome da trigger que voc deseja analisar execute o comando:


SELECT trigger_type, table_name, triggering_event FROM user_triggers WHERE trigger_name = 'nome_da_trigger';

Ou, se precisar obter o cdigo usado para gerar a trigger:


SELECT trigger_name, trigger_type, triggering_event, table_name, referencing_names, status, trigger_body FROM user_triggers WHERE trigger_name = 'nome_da_trigger';

Caso descubra que no precisa mais da trigger existe duas formas de tratar a situao. Eliminar a trigger ou desabilit-la. Eliminando a trigger:
DROP TRIGGER nome_da_trigger;

Caso prefira apenas desabilitar a trigger use o comando:


ALTER TRIGGER nome_da_trigger DISABLE;

Quando a trigger criada pela primeira vez ela habilitada automaticamente. Para habilitar a trigger novamente basta usar o comando:
ALTER TRIGGER nome_da_trigger ENABLE;

Mas vamos continuar criando nossas triggers. O prximo caso vai nos ajudar a impedir que algum cadastre um funcionrio fora do horrio de expediente:
CREATE TABLE nova_emp AS SELECT * FROM SCOTT.EMP; CREATE OR REPLACE TRIGGER hora_exp BEFORE INSERT ON nova_emp BEGIN IF (TO_CHAR(sysdate,'DY') IN ('SAB','DOM')) OR (TO_CHAR(sysdate,'HH24:MI') NOT BETWEEN '08:30' AND '17:30') THEN RAISE_APPLICATION_ERROR (-20500,'Voc s pode atualizar os empregados no horrio de expediente'); END IF; END; /

Essa trigger pode ser refinada para testar os predicados condicionais. Por exemplo:
CREATE OR REPLACE TRIGGER hora_exp BEFORE INSERT OR UPDATE OR DELETE ON nova_emp

BEGIN IF (TO_CHAR(sysdate,'DY') IN ('SAB','DOM')) OR (TO_CHAR(sysdate,'HH24:MI') NOT BETWEEN '08:30' AND '17:30') THEN IF DELETING THEN RAISE_APPLICATION_ERROR (-20500,'Voc s pode excluir empregados no horrio de expediente'); ELSIF INSERTING THEN RAISE_APPLICATION_ERROR (-20502,'Voc s pode incluir empregados no horrio de expediente'); ELSIF UPDATING ('SAL') THEN RAISE_APPLICATION_ERROR (-20504,'Voc s pode alterar salarios no horrio de expediente'); ELSE RAISE_APPLICATION_ERROR (-20506,'Voc s pode Fazer alteraes no horrio de expediente'); END IF; END IF; END; /

Vamos ver como usar valores OLD e NEW: Primeiro vamos criar uma tabela para conter os dados do nosso histrico.
CREATE TABLE DDUR (USUARIO VARCHAR2(15), HORARIO DATE, EMPNO NUMBER(4), ENAME VARCHAR2(10), JOB VARCHAR2(9), MGR NUMBER(4), HIREDATE DATE, SAL NUMBER(7,2), COMM NUMBER(7,2), DEPTNO NUMBER(2)) /

Agora vamos criar nossa trigger. Ela deve registrar tudo o que fizermos em nossa tabela.
CREATE OR REPLACE TRIGGER hist_emp AFTER INSERT OR UPDATE OR DELETE ON nova_emp FOR EACH ROW BEGIN INSERT INTO ddur VALUES( user, sysdate, :OLD.empno, :OLD.ename, :OLD.job, :OLD.mgr, :OLD.hiredate, :OLD.sal, :OLD.comm, :OLD.deptno); END; /

A referncia :OLD indica que estamos usando os valores antes da alterao. Caso quisssemos usar o valor atualizado a referencia seria :NEW. Ambas podem ser usadas na mesma trigger. Por exemplo, nossa trigger poderia ter sido escrita assim:
CREATE OR REPLACE TRIGGER hist_emp AFTER INSERT OR UPDATE OR DELETE ON nova_emp FOR EACH ROW BEGIN INSERT INTO ddur VALUES( user, sysdate, :NEW.empno, :NEW.ename, :OLD.JOB, :NEW.MGR, :OLD.HIREDATE, :OLD.sal, :OLD.comm, :OLD.deptno); END; /

Voc pode usar :OLD e :NEW em comparaes dentro da sua trigger, montando estruturas PL/SQL cada vez mais complexas. Por exemplo:

CREATE OR REPLACE TRIGGER aumento BEFORE UPDATE OF sal ON emp FOR EACH ROW BEGIN IF (:NEW.sal - :OLD.sal) < :OLD.sal * 0.025 THEN RAISE_APPLICATION_ERROR (-20512, 'Favor corrigir indice'); END IF; END;

No caso acima, se o aumento de salrio for inferior a 2,5% o sistema avisa que houve um erro na alterao salarial. Em um outro exemplo, mas agora com WHEN
CREATE OR REPLACE TRIGGER ver_sal BEFORE INSERT OR UPDATE OF sal, job ON empl FOR EACH ROW WHEN (NEW.job_id <> 'PRESIDENT') DECLARE v_minsal emp.sal%TYPE; v_maxsal emp.sal%TYPE; BEGIN SELECT MIN(sal), MAX(sal) INTO v_minsal, v_maxsal FROM emp WHERE job = :NEW.job; IF :NEW.sal < v_min OR :NEW.sal > v_maxsal THEN RAISE_APPLICATION_ERROR(-20515,'Salario invlido'); END IF; END; / UPDATE emp SET sal = 3400 WHERE ename = 'BLAKE';

Neste caso estamos garantido que ningum que for contratado com o cargo diferente de PRESIDENT ir receber um salrio menor que o menor salrio de seu cargo ou um salrio maior que o maior salrio de seu cargo. Uma trigger pode ser bem mais simples do que os exemplos acima. Por exemplo, se quisermos implementar uma restrio onde o salrio do funcionrio nunca possa ser reduzido, basta aplicarmos a trigger:
CREATE OR REPLACE TRIGGER veri_sal BEFORE UPDATE OF sal ON emp FOR EACH ROW WHEN (NEW.sal < OLD.sal) BEGIN RAISE_APPLICATION_ERROR (-20508, 'O salrio no pode ser reduzido'); END; /

Para que um usurio crie suas prprias triggers ele precisa ter o privilgio de sistema CREATE TRIGGER e ser o proprietrio da tabela onde ir criar a trigger. Caso no seja proprietrio ele deve ter o privilgio de sistema ALTER ou ALTER ANY TABLE. Caso precise criar triggers para eventos do banco de dados deve ter o privilgio de ADMINISTER DATABASE TRIGGER. Caso a trigger faa chamada de alguma procedure, quem estiver criando a trigger deve ter o privilgio de EXECUTE na procedure.

Como podemos notar as trigger podem ser usadas de forma bem flexvel. Com elas podemos gerar mecanismos de segurana mais flexveis, auditar dados de tabelas e implementar regras de negcios com mais facilidade.