Bancos de Dados I PUC-Rio Departamento de Informática (DI) Eng./Ciência da Computação e Sist. Informação Contrôle de Integridade em SGBDs: procedimentos armazenados, gatilhos e funções Prof. sergio@inf.puc-rio.br Sala 414 RDC Integridade semântica No modelo relacional, as integridades estruturais chave, entidade, domínio e referencial podem ser controladas com a criação das tabelas! Integridades semânticas (regras de negócio business rules) podem ser controladas na aplicação (não recomendado) ou por meio de visões (com check option), procedimentos armazenados (stored procedures ou SPs), gatilhos (triggers) e funções. 1
Sintaxe de Funções Sintaxe (varia de SGBD para SGBD): CREATE [OR REPLACE] FUNCTION nome_funcao [[parametro1 [IN OUT IN OUT] tipo], ] RETURN tipo AS [declaração de variáveis locais, cursores, etc] END [nome_funcao]; Funções - exemplo Base Carros Criar no SGBD uma função (armazenada!) que recebe como parâmetro um CGC de uma revendedora e um ano e retorne quantos veículos daquele ano especificado a revendedora, com este CGC, possui em estoque; 2
Funções sintaxe PostgreSQL CREATE OR REPLACE FUNCTION conta (CGC_in integer, ano_in char) RETURNS integer as $$ DECLARE total integer; select sum(quantidade) into total from garagens g where g.cgc = cgc_in and g.ano = ano_in; return total; END; $$ LANGUAGE plpgsql; Funções uso em SQL Revendedora Self Car / Ano 95 select * FROM conta (10100, '95'); Resultado: 10 Revendedora Dirija / Ano 92 select * FROM conta (10370, '92'); Resultado: 3 3
Sintaxe de Procedimentos Exemplo (também depende do SGBD) : CREATE [OR REPLACE] PROCEDURE nome_procedimento [[parametro1 [IN OUT IN OUT] tipo], ] AS [declaração de variáveis locais, cursores, etc] END [nome_procedimento]; Procedimentos - Exemplo Base CARROS Criar um procedimento armazenado que apresenta o seguinte relatório (considerando a tabela negócios): Revendedora Tiana ItaliaBarra EuroBarra Polux Total de Vendas R$350.000,00 R$600.000,00 NÃO VENDEU R$120.000,00 4
Procedimentos sintaxe PostgreSQL CREATE OR REPLACE FUNCTION relatorio() RETURNS void as $$ DECLARE nomec char(20); valorc numeric(16,2); meu_cursor cursor is select nome, valor from (select cgc, sum(preco) as valor from negocios group by cgc) totais, revendedoras where totais.cgc = revendedoras.cgc; OPEN meu_cursor; RAISE INFO 'REVENDEDORA - TOTAL DE VENDAS'; loop fetch meu_cursor into nomec, valorc; if not found then exit ; end if; RAISE INFO '% - %', nomec, valorc; end loop; CLOSE meu_cursor; END; $$ LANGUAGE plpgsql Exemplo: Sintaxe de Gatilhos CREATE [OR REPLACE] TRIGGER nome {BEFORE AFTER INSTEAD OF} {DELETE INSERT UPDATE OF col1,col2 } [OR DELETE INSERT UPDATE OF col1,col2... ] ON {Tabela visão} [REFERENCING OLD AS nome1 NEW AS nome2] [FOR EACH [ROW STATEMENT] [WHEN (condição)]... END [nome_trigger]; 5
Gatilhos- exemplo Base CARROS Criar um gatilho que, antes da realização de um negócio (venda de um automóvel de uma revendedora para um consumidor), verifique se o veículo que está sendo vendido de fato existe no estoque da revendedora. Gatilho sintaxe PostgreSQL CREATE OR REPLACE FUNCTION tem_estoque() -- primeiro uma função RETURNS trigger AS $tg_tem_estoque$ DECLARE qtd integer; qtd := 0; select quantidade into qtd from garagens g where g.cgc = New.cgc and g.codigo = New.codigo and g.ano = New.ano; RAISE INFO 'qtd= %', qtd; if (qtd <= 0) or qtd IS NULL then RAISE EXCEPTION 'Nao tem o carro % - ano % em estoque', NEW.codigo, NEW.ano; RETURN NULL; end if; RETURN NEW; END; $tg_tem_estoque$ LANGUAGE plpgsql; CREATE TRIGGER tg_tem_estoque BEFORE INSERT OR UPDATE ON negocios FOR EACH ROW EXECUTE PROCEDURE tem_estoque(); 6
Exemplo adicional Crie um trigger para armazenar na tabela LOG_VENDAS_ABAIXO_TAB as informações sobre negócios efetuados nos quais o preço de compra do veículo foi menor que o preço da tabela. Exemplo.: insert into negocios (cpf, cgc, codigo, ano, data, preco) values ('7685-5', 10150, 1111, '95','08/15/1995',8250.00) Solução CREATE OR REPLACE FUNCTION log_vendas() RETURNS trigger AS $tg_log_vendas$ DECLARE preco_tab numeric(15,2); select preco_tabela into preco_tab from automoveis a where a.codigo = NEW.codigo and a.ano = NEW.ano; if (NEW.preco < preco_tab) then insert into log_vendas_abaixo_tab(cpf,cgc,codigo,ano,data,diferenca) values(new.cpf,new.cgc,new.codigo,new.ano,new.data, preco_tab - New.preco); end if; RETURN NEW; END; $tg_log_vendas$ LANGUAGE plpgsql; CREATE TRIGGER tg_log_vendas BEFORE INSERT OR UPDATE ON NEGOCIOS FOR EACH ROW EXECUTE PROCEDURE log_vendas(); 7
Exemplo adicional 2 Crie um trigger para, após a realização de um negócio, atualizar a quantidade de veículos disponíveis em GARAGENS. Solução 2 CREATE OR REPLACE FUNCTION atualiza_estoque() RETURNS trigger AS $tg_atualiza_estoque$ update garagens g set quantidade = quantidade - 1 where g.cgc = New.cgc and g.codigo = New.codigo and g.ano = New.ano; RETURN NULL; END; $tg_atualiza_estoque$ LANGUAGE plpgsql; CREATE TRIGGER tg_atualiza_estoque AFTER INSERT ON NEGOCIOS FOR EACH ROW EXECUTE PROCEDURE atualiza_estoque(); 8