PROGRAMAÇÃO EM BANCO DADOS Stored Procedure e Trigger A tecnologia de banco de dados permite persistir dados de forma a compartilha-los com varias aplicações. Aplicação 1 aplicação 2 aplicação 3 SGDB Banco de Dados Por meio dos SGBD - Sistema Gerenciador de Banco de Dados - é possível gerenciar o acesso a dados em um BD. A SQL - linguagem de consulta estruturada -, é uma linguagem padrão ANSI para manipular dados por meio de um sistema gerenciador de banco de dados. A SQL pode ser dividida em 3 sub-linguagens: DDL - linguagem de definição de dados DCL - linguagem de controle de dados DML - linguagem de manipulação de dados Os SGDB devem disponibilizar recursos para manipulação de dados por meio de PL (linguagens procedimentais), já que a SQL é uma linguagem não procedimental. Por exemplo: Linguagem procedimental linguagem não procedimenta ---------------------------------------------------------------------------------------- Readln(y); select x,y Y= x*x; f rom raízes Println(x); where y = x*x Println(y); ---------------------------------------------------------------------------------------- 1
A programação em PL é feita por meio de function(função), Store Procedure (procedimentos armazenada) e Trigger (gatilho). Cada SGDB tem sua maneira própria para tratar esses elementos de programação. As sintaxes apresentadas a seguir referem-se ao ambiente Postgresql. Stored Procedure O postgresql emprega o conceito de function para definir stored procedure ( procedimentos armazenados) Sintaxe básica: Definição da função: ------------------------------------------------------------------------------------------------------------------ Create function nome_da_função (tipo_dado_variavel1, tipo_dado_variavel2, tipo_dado_variaveln) Return tipo AS '<corpo da função>' LANGUAGE `nome da linguagem usada no corpo` -------------------------------------------------------------------------------------------------------------- Dentre as linguagens possíveis para o postgresql temos: SQL PLPGSQL C PERL TEL PYTHONU uso da função: --------------------------------------------------------------------------------------------------------------------------------- nome_da_função(valor_para_variavel1,valor_para_variavel1,..., valor_para_variaveln) --------------------------------------------------------------------------------------------------------------------------------- A aspa no nome da linguagem pode ser opcional, depende da versão do Postgresql (no 8.3 ela já é opcional) Quando definimos que a linguagem de uma função e sql, LANGUAGE SQL, só podemso empregar comandos da sql em seu corpo. 2
A definição do corpo da função depende da linguagem para plpgsql, devendo ser delimitada por aspas simples ' ou cifrão duplo $$. Obedece a seguinte sintaxe: Uso de ' para delimitar o corpo da função 'declare variavel1 tipo_da_varialvel1, variavel2 tipo_da_variavel2,... BEGIN INSTRUÇÃO 1 INSTRUÇÃO 2 RETURN VALOR RETORNO END;' Uso de '$$ para delimitar o corpo da função $$declare variavel1 tipo_da_varialvel1, variavel2 tipo_da_variavel2,... BEGIN INSTRUÇÃO 1 INSTRUÇÃO 2 RETURN VALOR RETORNO END;$$ Obs: 1 a declaração de variáveis, por meio da palavra reservada declare, é opcional 2 - Para que uma função retorne mais de um valor, mais de uma linha de uma tabela, o tipo de dado no retorno return tipo deve ser SETOF nome_tabela 3 - As variáveis que correspondem aos n-valores passados como parâmetros para a função devem ser referenciadas como $1, $2, $3,.. $n 4 Quando o corpo da função for definido com o uso de aspas simples devemos delimitar a função com $$ para que o compilador não interprete a aspa simples do corpo da função como fim da função. Exemplos: 1 - Função sem parâmetros de entrada primeiro: definição da função Create function dois() returns integer as `select 2; language sql; segundo: uso da função Select dois(); 3
2 - função com parâmetros de entrada primeiro: criação da função create funcition media(decimal,decimal) returns decimal as $$ select ($1+$2)/2; $$ language sql; segundo: uso da função caso1 select media(10,8); caso2 select media(8,9); 3 - Função que totaliza os valores de itens de uma nota de venda da tabela item_nota CREATE FUNCTION totalnota(integer) RETURNS INTEGER AS ' SELECT SUM(preco) FROM item_nota WHERE numnota = $1; ' LANGUAGE 'SQL'; 4 Função maior salário de tabela funcionário Etapas: 4.1 criar a tabela create table func(cod integer, nome varchar(30), salário decimal(10,2), comissao decimal(10,2)); 4.2 inserir dados na tabela criada insert into func values(1, 'ZE',1000,500); insert into func values(2, 'IVO',1500,200); insert into func values(3, 'EVA',1800,100); 4.3 crie uma função maior_salario, para exibir o maior salario create function maior_salario() returns decimal as 'select Max(salario) from func;' language sql; 4
4.4 usar a função que retorna o maior salario select maior_salario(); 5 - Função que concatena textos Create function soma(text, text) returns char as $$ Declare resultado text; Begin resultado := $1 $2; return resultado; End;$$ language 'plpgsql'; usando a função: select soma('sidney ','Silva'); 6 Função que retorna o nome dos clientes que fizeram cadastro hoje( data corrente) ' ' CREATE FUNCTION cadastro_hoje() RETURNS SETOF clientes AS SELECT nome FROM clientes WHERE datacadastro = current_date; LANGUAGE 'SQL'; 7- Função que disponibilize o maior valor de venda. create function maiorvenda() returns decimal as 'select Max(valor) from venda;' language sql; usando a função: select maiorvenda(); 8- Função que disponibilize o valor total obtido com vendas create function valortotal() returns decimal as 'select sum(valor) from venda;' language sql; usando a função: select valortotal(); 9- Crie um store procedure que disponibilize data da ultima venda 5
create function dataultimavenda() returns date as 'select max(data) from venda;' language sql; usando a função: select dataultimavenda(); TRIGGER Uma função de gatilho, trigger, é uma ação que o SGBD deve desencadear antes ou depois de um dos seguintes comandos: insert, update ou delete. Sintaxe: Create trigger nome_gatilho {before ou after} {evento[or evento]} on table for each {row ou STATEMENT } execute procedure nome_função (v1,v2,...,vn) Em gatilho onde tem-se FOR EACH ROW o procedimento em execute procedure é chamado uma vez para cada linha que a operação modifica. Já se tivermos FOR EACH STATEMENT ocorrerá somente uma chamada ao procedimento descrito em execute procedure, não importando quantas linhas sejam modificadas. Variáveis disponíveis no ambiente de programação - PL NEW Tipo de dado RECORD; variável contendo a nova linha do banco de dados, para as operações de INSERT/UPDATE nos gatilhos no nível de linha. OLD Tipo de dado RECORD; variável contendo a antiga linha do banco de dados, para as operações de UPDATE/DELETE nos gatilhos no nível de linha.. TG_NAME Tipo de dado name; variável contendo o nome do gatilho disparado. TG_WHEN Tipo de dado text; uma cadeia de caracteres contendo BEFORE ou AFTER, dependendo da definição do gatilho. TG_LEVEL Tipo de dado text; uma cadeia de caracteres contendo ROW ou STATEMENT, dependendo da definição do gatilho. TG_OP Tipo de dado text; uma cadeia de caracteres contendo INSERT, UPDATE, ou DELETE, informando para qual operação o gatilho foi disparado. TG_RELID Tipo de dado oid; o ID de objeto da tabela que causou o disparo do gatilho. 6
TG_RELNAME Tipo de dado name; o nome da tabela que causou o disparo do gatilho. TG_NARGS Tipo de dado integer; o número de argumentos fornecidos ao procedimento de gatilho na instrução CREATE TRIGGER. TG_ARGV[] Tipo de dado matriz de text; os argumentos da instrução CREATE TRIGGER. O contador do índice começa por 0. Índices inválidos (menor que 0 ou maior ou igual a tg_nargs) resultam em um valor nulo. Exemplos: 1 Gatilho que após efetuar uma inclusão, alteração ou exclusão na tabela func armazema dados para auditoria na tabela func_auditoria Primeiro criar a tabela func_auditoria --------------------------------------------------------------------------------------------------------------- create table func_auditoria( operacao varchar(1), usuario varchar(30), data timestamp, nome_func varchar(40), salario decimal(10,2)); ------------------------------------------------------------------------------------------------------------- Segundo criar a função para o gatilho --------------------------------------------------------------------------------------------------------------- CREATE FUNCTION processa_audit_func() RETURNS TRIGGER AS $$ BEGIN -- -- Cria uma linha na tabela func_auditoria para refletir a operação -- realizada na tabela func. Utiliza a variável especial TG_OP -- para descobrir a operação sendo realizada. -- IF (TG_OP = 'DELETE') THEN INSERT INTO func_auditoria values('e', user, now(), OLD.nome, OLD.salario); RETURN OLD; ELSIF (TG_OP = 'UPDATE') THEN INSERT INTO func_auditoria values('a', user, now(), OLD.nome, OLD.salario); RETURN NEW; ELSIF (TG_OP = 'INSERT') THEN INSERT INTO func_auditoria values ('I', user, now(), NEW.nome, NEW.salario); RETURN NEW; 7
END IF; RETURN NULL; -- o resultado é ignorado uma vez que este é um gatilho AFTER END; $$ language plpgsql; ----------------------------------------------------------------------------------------------------------------- Terceiro criar o gatilho --------------------------------------------------------------------------------------------------------------------- CREATE TRIGGER funcionario_audit AFTER INSERT OR UPDATE OR DELETE ON func FOR EACH ROW EXECUTE PROCEDURE processa_audit_func(); ---------------------------------------------------------------------------------------------------------------- Quando algum usuario fizer um insert, update ou delete na tabela func o SGBD registrará os dados na tabela func_auditoria Exemplo: se fizermos uma conexão com o sgbd com o usuario postgres e atualizar a tabela func com o comando:: update func set salario = 1200.00 where cod = 1; podemos consulta a tabela func_auditoria: SELECT * FROM func_auditoria; Teremos como resultado: ----------------------------------------------------------------------- "A";"postgres";"2009-10-09 17:09:43.258";"ZE";1000.00 ------------------------------------------------------------------------ 2 Gatilho que antes de efetuar uma inserção na tabela func verifica se o nome do funcionario é nulo e se o salario é nulo ou menor do que zero caso seja não faz a inclusão e emite uma mensagem Primeiro definição da função de gatilho ------------------------------------------------------------------------------------ CREATE FUNCTION func_gatilho() RETURNS trigger AS $$ 8
BEGIN -- Verificar se foi fornecido o nome do empregado IF NEW.nome IS NULL THEN RAISE EXCEPTION 'O nome do empregado não pode ser nulo'; END IF; IF NEW.salario IS NULL THEN RAISE EXCEPTION 'O não pode ter um salário nulo', NEW.nome_emp; END IF; -- Quem paga para trabalhar? IF NEW.salario < 0 THEN RAISE EXCEPTION 'O funcionario não pode ter um salário negativo', NEW.nome_emp; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; ---------------------------------------------------------------------------------------------------------------- Segundo: criação do gatilho para a tabela func ----------------------------------------------------------------------------------------------------------------- CREATE TRIGGER Novo_func BEFORE INSERT OR UPDATE ON func FOR EACH ROW EXECUTE PROCEDURE func_gatilho(); ---------------------------------------------------------------------------------------------------------------- terceiro: o uso Quando se tenta inserir: insert into func(cod,salario) values(5,1800); O SGBD emitirá a mensagem: -------------------------------------------------------------------------------------- ERROR: O nome do empregado não pode ser nulo. ------------------------------------------------------------------------------------ 9
3 Gatilho que ao se efetuar uma inserção na tabela venda armazena na tabela data os seguintes valores usuario que realizou o login, a palavra "venda" e a data com hora na atual. Create trigger auditoria after insert on venda for each Statement execute procedure inseriu(); Create function inseriu() returns trigger as $$ begin insert into data values (user,'vendas',now()); return null; end; $$ language pspgsql; Create table data(usuario varchar(50), tabela varchar(50), Datahora timestamp); 4 - Crie um gatilho que a cada mudança no preço de venda de um produto armazene os dados na tabela histórico. Create trigger atualiza_preco_venda after update on produto for each row execute procedure incluir_historico(); Create function incluir_historico() returns trigger as begin if (old.valor_venda<>new.valor_venda) then insert into historico (data, user, valor_anterior, valor_atual) values (now(),user,old.valor_venda, new.valor_venda); return null; end;' language plpgsql; 10