BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com - Aula 11 - TRIGGERS (GATILHOS OU AUTOMATISMOS) 1. INTRODUÇÃO Desenvolver uma aplicação para gerenciar os dados significa criar uma aplicação que faça o controle sobre todo ambiente desde a interface, passando pela manutenção dos dados e as regras de negócios do sistema. Assim, é o sistema que deve controlar e tomar as decisões sobre o que fazer em determinadas situações. Em um programa para controlar os produtos de uma empresa, quando a quantidade de um produto atingir certa quantidade o sistema deverá avisar o operador/usuário para providenciar a reposição do mesmo. Ao trabalhar com base de dados Cliente/Servidor como SQL Server, Oracle, Informix, PostgreSQL dentre outras, é possível usar um recurso muito poderoso chamado Trigger. 2. TRIGGER Triggers são rotinas armazenadas no banco de dados e utilizadas quando um comando Insert, Update ou Delete é executado em uma tabela ou até mesmo uma visão (view). Trigger significa gatilho e é disparada quando os comandos da DML são disparados. São executadas automaticamente sem a interferência do usuário, ou seja, são procedimentos armazenados que são acionados por algum evento e em determinado momento. As principais aplicações das triggers estão relacionadas às validações, restrições de acesso, rotinas de segurança e consistência de dados. Triggers são iguais a stored procedures com as seguintes exceções: - São chamadas automaticamente; - Não tem parâmetros; - Não retornam valores. A criação de uma trigger envolve duas etapas: - Um comando SQL que vai disparar o Trigger (INSERT, DELETE, UPDATE) - A ação que a trigger vai executar (Geralmente um bloco de códigos SQL) O gatilho fica associado com uma tabela e executa uma função especifica. Na maioria dos bancos de dados estes eventos podem ser inserções (INSERT), atualizações (UPDATE) e exclusões (DELETE), e podem ser executadas em dois momentos: - Antes da execução do evento (BEFORE); - Depois da execução do evento (AFTER). Uma trigger é executada automaticamente mediante seis eventos da DML: - BEFORE UPDATE (Antes da atualização); - AFTER UPDATE (Após a atualização); - BEFORE INSERT (Antes da inserção); 1 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com - AFTER INSERT (Após a inserção); - BEFORE DELETE (Antes da exclusão); - AFTER DELETE (Após a exclusão); 2.1. BEFORE ou UPDATE Uma trigger deve ser disparada antes de o registro ser atualizado caso queira alterar o valor de uma ou mais colunas antes que a linha seja atualizada, ou queira bloquear a alteração da linha gerando uma exceção, como por exemplo: usar uma trigger BEFORE UPDATE para evitar que o usuário apague o registro de um cliente que tenha comprado nos últimos dois anos. Triggers do tipo AFTER quando se deseja garantir que a atualização que disparou a trigger seja completada com sucesso antes de executar outras ações, como por exemplo: inserir uma linha em uma tabela de salario_historico sempre que o salário de um funcionário for alterado. 2.2. Usando OLD e NEW No corpo de uma trigger é possível usar tanto os valores antigos ou novos de qualquer registro. Para isso basta utilizar os comandos OLD e NEW. EXEMPLO: old.aut_nome new.aut_nome Com esses comandos é possível criar registros de histórico, calcular percentual de alteração de valores, etc. 3. TRIGGERS NO POSTGRESQL Um diferencial das triggers no PostgreSQL é que elas são sempre associadas a funções de triggers e em outros Bancos de Dados elas são criadas no corpo da trigger. 3.1. Tipos de Triggers O PostgreSQL possui dois tipos de triggers: - Triggers-por-linha - Triggers-por-instrução A trigger-por-linha é disparada uma vez para cada registro afetado pela instrução que disparou a trigger. A trigger-por-instrução é disparada somente uma vez quando a instrução é executada. 2 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com 3.1.1. FUNÇÕES DE TRIGGER E LINGUAGENS PROCEDURAIS Funções de triggers são funções que não recebem nenhum parâmetro e retornam o tipo trigger. Essas funções recebem uma estrutura chamada TriggerData, e esta é passada internamente para a função pelo PostgreSQL. O PostgreSQL disponibiliza duas variáveis importantes para serem usadas em conjunto com as triggers-por-linha: NEW e OLD. A variável NEW, no caso do INSERT, armazena o registro que está sendo inserido. No caso do UPDATE, armazena a nova versão do registro depois da atualização. A variável OLD, no caso do DELETE, armazena o registro que está sendo excluído. No caso do UPDATE, armazena a antiga versão do registro depois da atualização. As funções de triggers devem ser escritas em alguma linguagem procedural disponível no banco de dados1. Cada linguagem, que suporta triggers, possui o seu próprio método para tornar os dados de entrada da trigger disponíveis para a função. Estes dados de entrada incluem o tipo de evento da trigger, assim como as opções informadas na criação da trigger. Para uma trigger no nível de linha, os dados de entrada também incluem as linhas NEW para as triggers de INSERT e UPDATE, e a linha OLD para os triggers de UPDATE e DELETE. SINTAXE: CREATE TRIGGER nome { BEFORE | AFTER } { evento [OR ...] } ON tabela FOR EACH { ROW | STATEMENT } EXECUTE PROCEDURE função ( argumentos ) Onde: Nome: O nome da Trigger. Tabela: O nome da tabela na qual a trigger estará vinculada. Evento: INSERT, DELETE ou UPDATE. Função: Pode-se usar, neste caso, uma stored procedure para execução de determinada tarefa. row|statement: especifica se a trigger deve ser disparada uma vez para cada linha afetada pelo evento ou apenas uma vez por comando SQL. Se não for especificado nenhum dos dois, o padrão é FOR EACH STATEMENT. EXEMPLO: Para este primeiro exemplo é necessário alterar nosso banco de dados: ALTER TABLE autor ADD aut_cpf CHAR(14); 1 Essas linguagens podem ser várias, como Ruby, Perl, Python, entre outras. Atualmente existem quatro linguagens procedurais disponíveis na distribuição padrão do PostgreSQL: PL/pgSQL, PL/Tcl, PL/Perl e PL/Python. Mas é possível que o usuário defina outras linguagens. Para instalar novas linguagens no PostgreSQL, consulte a documentação oficial do PostgreSQL, que pode ser obtida no seguinte endereço: http://pgdocptbr.sourceforge.net/. 3 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com Próximo passo é criar a função que faz a inserção de determinado valor nesta tabela: CREATE OR REPLACE FUNCTION ftFormataCPF() RETURNS trigger AS $$ BEGIN IF char_length(new.aut_cpf) = 11 THEN new.aut_cpf := SUBSTRING(new.aut_cpf FROM 1 FOR 3) || '.' || SUBSTRING(new.aut_cpf FROM 4 FOR 3) || '.' || SUBSTRING(new.aut_cpf FROM 7 FOR 3) || '-' || SUBSTRING(new.aut_cpf FROM 10 FOR 2); return new; END IF; END; $$ LANGUAGE plpgsql; Por fim, criando a trigger: CREATE TRIGGER tFormataCpf BEFORE INSERT ON autor FOR EACH ROW EXECUTE PROCEDURE ftFormataCPF (); Verificando seu funcionamento: INSERT INTO autor (aut_id, aut_nome, aut_cpf) VALUES (102, 'teste function2', '00000000002'); 3.1.2. PARÂMETROS ESPECIAIS Dentre as funções que foram apresentadas para triggers é possível utilizar uma grande quantidade de argumentos com a finalidade de criar controles e métodos de auditoria nos bancos de dados PostgreSQL. Todas elas são facilmente encontradas no manual do PostgreSQL. Desses, um dos argumentos mais importantes é o TG_OP que permite identificar qual operação está sendo realizada: INSERT, UPDATE, DELETE. Muito útil para criar mecanismos de auditoria nos dados contidos em determinada tabela. Acompanhe atentamente o exemplo abaixo: EXEMPLO: Primeiramente vamos criar uma tabela de histórico: CREATE TABLE historico_cd ( hcd_id INTEGER, hcd_preco_novo NUMERIC(14,2), hcd_preco_antigo NUMERIC(14,2), hcd_data_hora TIMESTAMP, hcd_operacao VARCHAR(50) ); Depois criamos a função: 4 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com CREATE OR REPLACE FUNCTION auditaCD() RETURNS trigger AS $$ BEGIN IF (TG_OP = 'INSERT') THEN INSERT INTO historico_cd (hcd_id, hcd_preco_novo, hcd_preco_antigo, hcd_data_hora, hcd_operacao) VALUES (NEW.cd_id, NEW.cd_preco, NULL, CURRENT_TIMESTAMP, ‘INSERT’); RETURN NEW; ELSIF (TG_OP = 'UPDATE') THEN INSERT INTO historico_cd (hcd_id, hcd_preco_novo, hcd_preco_antigo, hcd_data_hora, hcd_operacao) VALUES (OLD.cd_id, NEW.cd_preco, OLD.cd_preco, CURRENT_TIMESTAMP, ‘UPDATE’); RETURN NEW; ELSIF (TG_OP = 'DELETE') THEN INSERT INTO historico_cd (hcd_id, hcd_preco_novo, hcd_preco_antigo, hcd_data_hora, hcd_operacao) VALUES (OLD.cd_id, NULL, OLD.cd_preco, CURRENT_TIMESTAMP, ‘DELETE’); RETURN NEW; END IF; END; $$ LANGUAGE plpgsql; Por fim, criamos a Trigger: CREATE TRIGGER tg_auditaCD AFTER INSERT OR UPDATE OR DELETE ON cd FOR EACH ROW EXECUTE PROCEDURE auditaCD(); Verificando seu funcionamento: INSERT INTO cd (cd_id, grav_id, cd_nome, cd_preco, cd_dt_lancamento) VALUES (100, 1, 'teste trigger1', 10.00, '2009-11-15'); UPDATE cd SET cd_preco = 20.00 WHERE cd_id = 100; DELETE FROM cd WHERE cd_id = 100; 3.1.3. TRIGGERS RECURSIVAS Se uma função de trigger executar comandos SQL, estes comandos podem disparar triggers novamente. Isto é conhecido como cascatear triggers. Não existe limitação direta do número de níveis de cascateamento. É possível que o cascateamento cause chamadas recursivas da mesma trigger; por exemplo, um trigger para INSERT pode executar um comando que insere 5 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com uma linha adicional na mesma tabela, fazendo com que a trigger para INSERT seja disparada novamente. É responsabilidade do programador evitar recursões infinitas nestes casos. 3.1.4. ALTERANDO UMA TRIGGER É possível alterar o nome de trigger usando a sintaxe abaixo: SINTAXE: ALTER TRIGGER nome ON tabela RENAME TO novo_nome; 3.1.5. EXCLUINDO UMA TRIGGER Para excluir uma trigger basta executar o comando abaixo: SINTAXE: DROP TRIGGER nome ON tabela [ CASCADE | RESTRICT ] Onde: Nome: é o nome da trigger a ser removida. Tabela é o nome da tabela para a qual a trigger está definida. [ CASCADE | RESTRICT ]: indica que ao remover a trigger vamos remover também todos os objetos que dependem dela (CASCADE) ou recusaremos sua exclusão (RESTRICT). 3.1.6. DESABILITANDO/HABILITANDO UMA TRIGGER Para desabilitar/habilitar uma trigger execute o comando abaixo: SINTAXE: ALTER TABLE nome_tabela [DISABLE|ENABLE] TRIGGER nome_trigger; Para desabilitar todas as triggers da tabela, execute o seguinte comando: SINTAXE: ALTER TABLE nome_tabela [DISABLE|ENABLE] TRIGGER ALL; 4. EXERCÍCIOS a) Crie uma TRIGGER responsável por verificar, no momento da inserção na tabela de CD, se o preço do CD é maior de 1,00 e inferior a 100,00. 5. REFERÊNCIAS ELMASRI, Ramez; Sistema de Banco de Dados - Fundamentos e Aplicações - 4ª Edição, Pearson Education, 2005 6 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com OLIVEIRA, Celso Henrique Poderoso; SQL – Curso Prático; Novatec, 2002. Momjian, Bruce; PostgreSQL: Introduction and Concepts. E-Book; Addison-Wesley, 2001. PostgreSQL – Manual (With user comments) do PostgreSQL 8.3. Disponível em <http://www.postgresql.org/files/documentation/pdf/8.3/postgresql-8.3-A4.pdf> 7