Você está na página 1de 61

Programao em SGBD

degas@uesc.br http://alvarodegas.cjb.net/

Programao em SGBD

Tabelas do novo BD_Empresa

Empregado(nominic, nomint, nomefim, idemp, empdtnasc, ender, empsexo, salario, idsuper, iddep) Departamento (nomedep, iddep, idchefe, dtinchef) Projeto (nomeproj, idproj, locproj, iddep) Dependente (idemp, depend, depsexo, depdtnasc, tipo) LocalDepto (iddep, locdep) TrabalhaEm (idemp, idproj, horas)

Chaves e suas restries

Chaves primrias

idemp: varia de 101 at 999

Automtico Automtico Automtico

iddep

idproj: varia de 10 at 99

Chaves estrangeiras

idsuper e idchefe matrcula do supervisor do empregado e do chefe do departamento em Projeto: departamento controlador em Empregado: departamento de lotao

iddep

Chaves e suas restries

Estas restries s podem ser idemp: varia de 101 at 999 implementadas Automtico atravs de iddep programao Automtico idproj: varia de 10 at 99 de gatilhos Automtico (triggers), e usando chaves estrangeiras: idsuper e idchefe matrcula do supervisor estruturas do adicionais empregado e do chefe do departamento chamadas iddep em Projeto: departamento controlador sequncias (sequences) em Empregado: departamento de lotao chaves primrias:

Novos Scripts

De criao das tabelas

newcreatetables.sql inserttables.sql

De povoamento das tabelas

De colocao da integridade referencial

newaltertables.sql newconsultas.sql

Algumas consultas

Reformulando o BD Empresa

Scripts: arquivos com comandos SQL em sequencia

/i <nome_do_arquivo>
calcs c:/UESC/Ensino/bd1/bd_empresa/scripts/createdatabase.sql \i c:/UESC/Ensino/bd1/bd_empresa/scripts/newcreatedatabase.sql \i c:/UESC/Ensino/bd1/bd_empresa/scripts/droptables.sql \i c:/UESC/Ensino/bd1/bd_empresa/scripts/newcreatetables.sql \i c:/UESC/Ensino/bd1/bd_empresa/scripts/newinserttables.sql \i c:/UESC/Ensino/bd1/bd_empresa/scripts/newaltertables.sql \i c:/UESC/Ensino/bd1/bd_empresa/scripts/newconsultas.sql

Arquivos com extenso .sql


Problemas adicionais

As chaves dos empregados, dos departamentos e dos projetos

Devem ser geradas automaticamente

E sequencialmente

Tem restries de valor


CREATE SEQUENCE <nome_sequencia> {INCREMENT i} {MINVALUE min} {MAXVALUE max} {START s} {CYCLE};

Sequencias (sequences)

Usando uma sequncia

NEXTVAL('nome_sequencia')

Incrementa a sequencia (usando seu incremento) Retorna o valor Recupera o ultimo valor gerado Altera o valor

CURRVAL('nome_sequencia')

SETVAL('nome_sequencia', valor)

Exemplo

CREATE SELECT 1 SELECT 2 SELECT 2 SELECT 100 SELECT 101 SELECT

SEQUENCE Empregado_idemp_seq; nextval('Empregado_idemp_seq'); nextval('Empregado_idemp_seq'); currval('Empregado_idemp_seq'); setval('Empregado_idemp_seq', 100); nextval('Empregado_idemp_seq'); * FROM Empregado_idemp_seq;

No BD_Empresa

Trs tabelas tem chave primria autoincrementada


idemp INTEGER NOT NULL PRIMARY KEY, iddep idproj SMALLINT NOT NULL PRIMARY KEY, INT NOT NULL PRIMARY KEY,

Criar as sequncias:

CREATE SEQUENCE empregado_idemp_seq; CREATE SEQUENCE departamento_iddep_seq; CREATE SEQUENCE projeto_idproj_seq;

Ajustando

As sequncias no podem gerar chaves repetidas

SELECT max(idemp) FROM empregado; SELECT max(iddep) FROM departamento; SELECT max(idproj) FROM projeto; SELECT setval('empregado_idemp_seq', 112); SELECT setval('departamento_iddep_seq', 5); SELECT setval('projeto_idproj_seq', 19);

Precisamos fazer os comandos abaixo (porque?)


Mais problemas adicionais


Instruir o SGBD usar valor da sequncia a cada incluso

Um gatilho, que dispara antes da incluso Este gatilho altera o valor da chave primria usando a sequencia correta

Como programar gatilhos?

Procedimentos

Procedimentos armazenados Stored procedures Implementam a lgica procedimental

Armazenado no esquema do banco de dados

Podem possuir parmetros de entrada e/ou sada

Procedimentos

Porque usar?

Regras de negcio (acesso a dados) Camada de acesso a dados Encapsulamento (acoplamento baixo entre BD e Programas)

Procedimentos

Procedimentos armazenados incluem Comandos DML

SELECT, INSERT, UPDATE, DELETE IF..THEN..ELSE, FOR SELECT..DO WHILE..DO, etc.

Comandos de fluxo de controle como

Tratamento de excees e manipulao de erros

Estrutura Padro SQL


CREATE PROCEDURE <p_nome> [ ( <v_ent1><etipo1> {, <v_ent_i><e_tipo_i> } ) ] [RETURNS ( <v_sai_1> <stipo_1> {, <v_sai_i> <s_tipo_i> } ) ] AS {DECLARE VARIABLE <v_loc_i> <l_tipo_i;} BEGIN /* instrues do procedimento */ END

Funes no PostGres

PostGres

Implementa-se uma variao particular de stored procedures com funes


Quantidade variada de linguagens Boa fonte de consulta sobre o postgreSQL:

http://pgdocptbr.sourceforge.net/pg80/

Funes Language SQL

admite apenas comandos SQL admite comandos de fluxo

Funes Language PlPgSQL

Language SQL - Uma funo trivial


CREATE FUNCTION soma (INTEGER, INTEGER) RETURNS INTEGER AS ' SELECT $1 + $2; ' LANGUAGE 'SQL';

select soma(2,3);

Algo mais valioso

Calcular o salario total dos empregados de um departamento

CREATE or REPLACE FUNCTION SalarioTotal(INT) RETURNS FLOAT AS $$ SELECT SUM(salario) FROM Empregado WHERE iddep = $1; $$ LANGUAGE 'SQL'; select salariototal(3);

Nomes dos empregados lotados num departamento (pelo nomedep)


CREATE OR REPLACE FUNCTION EmpregadosDepto (VARCHAR(15)) RETURNS setof VARCHAR(20) as $$ SELECT nominic || ' ' || nomint || ' ' || nomefim AS nome FROM Empregado WHERE iddep IN ( SELECT iddep FROM Departamento WHERE nomedep = $1 ); $$ LANGUAGE 'SQL'; select EmpregadosDepto('Pesquisa'); Select * from EmpregadosDepto('Pesquisa');

Nomes dos empregados lotados num departamento (pelo nomedep)


CREATE OR REPLACE FUNCTION EmpregadosDepto (VARCHAR(15)) RETURNS setof VARCHAR(20) as $$ SELECT nominic || ' ' || nomint || ' ' || nomefim AS nome FROM Empregado WHERE iddep IN ( Aqui um bom exemplo SELECT iddep de encapsulamento. FROM Departamento Caso a estrutura da WHERE nomedep = $1 tabela Empregado ); mude, isto no impactar $$ nas camadas mais externas! LANGUAGE 'SQL'; select EmpregadosDepto('Pesquisa'); Select * from EmpregadosDepto('Pesquisa');

Aniversariantes do dia (ordem alfabtica)


CREATE OR REPLACE FUNCTION niverdia() RETURNS SETOF varchar(20) AS $$ SELECT nominic || ' ' || nomint || ' ' || nomefim AS nome FROM Empregado WHERE EXTRACT(MONTH FROM empdtnasc)= EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(DAY FROM empdtnasc) = EXTRACT(DAY FROM CURRENT_DATE) ORDER BY nome; $$ LANGUAGE 'SQL'; select * from niverdia();

Aumento de salrio (percentual)


CREATE OR REPLACE FUNCTION AumentaSal(double precision) RETURNS void as $$ Update empregado Set salario = salario*$1 $$ LANGUAGE 'SQL'; select AumentaSal(1.06); --6% de aumento

Copianto dados entre tabelas


CREATE Table TempTab ( idemp int not null, idproj int not null, salario float, deplot smallint, depcont smallint, constraint fk_Emp_id foreign key (IDEMP)references Empregado, constraint fk_Proj_id foreign key (IDPROJ) references Projeto, PRIMARY KEY (idemp, idproj) ); CREATE FUNCTION PovoaTempTab() RETURNS void AS $$ INSERT INTO TempTab SELECT idemp,idproj,salario,deplot, depcont FROM (select idemp, salario,iddep AS deplot FROM Empregado) E NATURAL JOIN TrabalhaEm NATURAL JOIN (SELECT idproj, iddep AS depcont FROM Projeto) P; $$ LANGUAGE SQL; select * from PovoaTempTab (); select * from TempTab order by idemp, idproj;

Usando comandos de fluxo

Language plpgSQL

Oracle PL/SQL Admite instrues do tipo DML

SELECT, INSERT, UPDATE, DELETE IF..THEN..ELSE..END IF FOR..SELECT..LOOP..END LOOP WHILE..LOOP..END LOOP

Comandos de fluxo de controle como


Tratamento de excees e manipulao de erros

plpgsql: Mais legibilidade


CREATE FUNCTION horasDepto(dep int) RETURNS real LANGUAGE plpgsql AS $$ DECLARE total integer := 0; BEGIN total := (SELECT SUM(horas) FROM TrabalhaEm NATURAL JOIN Empregado WHERE iddep = dep); RETURN total; END; $$ ;

Tratamento de Erros

Instruo SQL pode executar com sucesso ou no SGBD retorna uma mensagem (valor) Categorias de mensagens:

SQLERROR valores menores que zero SUCESS valor igual a zero NOT FOUND valor igual a 100 nenhum registro foi recuperado pela instruo SQL

Erros em plpgSQL

Instruo RAISE relatar erros e mensagens


RAISE nivel 'formato' [,variavel [, ...]];

Nveis possveis:

DEBUG, LOG, INFO, NOTICE, WARNING e EXCEPTION RAISE EXCEPTION administra um erro que aborta a transao corrente

Os outros nveis somente geram mensagens (warnings)

RAISE EXCEPTION

Suponha que o comando


INSERT INTO Dependente VALUES (113, 'Luana', 'F', '05-APR-1998', 'filha');

Passa pela instruo


RAISE EXCEPTION 'Se %, que seja menor que 14 anos: %', NEW.tipo, NEW.depdtnasc;

Isto abortar a transao


ERRO: Se filha, que seja menor que 1996-04-05 SQL state: P0001 14 anos:

RAISE EXCEPTION gera sempre o mesmo cdigo SQLSTATE, P0001

RAISE EXCEPTION

Suponha que o comando NEW ser mostrado INSERT INTO Dependente VALUES (113, 'Luana', junto com gatilhos
'F', '05-APR-1998', 'filha');

Passa pela instruo


RAISE EXCEPTION 'Se %, que seja menor que 14 anos: %', NEW.tipo, NEW.depdtnasc;

Isto abortar a transao


ERRO: Se filha, que seja menor que 1996-04-05 SQL state: P0001 14 anos:

RAISE EXCEPTION gera sempre o mesmo cdigo SQLSTATE, P0001

Desvio e Repetio
IF boolean-expression THEN statements [ ELSIF boolean-expression THEN statements [ ELSIF boolean-expression THEN statements ...]] [ ELSE statements ] END IF;

WHILE boolean-expression LOOP statements END LOOP;

IF boolean-expression THEN result := 'zero'; statements ELSIF number > 0 THEN [ ELSIF boolean-expression THEN statements result := 'positive'; [ ELSIF boolean-expression THEN ELSIF number < 0 THEN statements result := 'negative'; ...]] ELSE [ ELSE statements ]-- number is null? result := 'NULL'; END IF;

Desvio e Repetio IF number = 0 THEN

END IF;

WHILE boolean-expression LOOP statements END LOOP;

e Repetio WHILEDesvio amount_owed > 0 AND IF boolean-expression THEN > 0 LOOP gift_balance statements -- some computations here [ ELSIF boolean-expression THEN END LOOP; statements
[ ELSIF boolean-expression THEN statements WHILE NOT done LOOP ...]] -- some computations [ ELSE END LOOP; statements ] END IF;

here

WHILE boolean-expression LOOP statements END LOOP;

FOR LOOP

Para executar uma repetio

FOR var IN [ REVERSE ] expression .. expression [ BY expression ] LOOP statements END LOOP;

Pode repetir em funo de uma consulta (select)


statements

FOR target IN query LOOP END LOOP;

FOR LOOP

Para executar uma repetio

FOR var IN [ REVERSE ] expression .. expression [ BY expression ] LOOP statements END LOOP;

FOR xfuno in 1..10 LOOP Pode repetir em de uma consulta (select) END LOOP; FOR target IN query LOOP

statements END LOOP;

FOR LOOP
Para executar s uma repetio DECLARE RECORD; FOR var IN [ REVERSE ] expression .. expression ... BY expression ] LOOP FOR s in select distinct sal statements from empregado END LOOP; order by 1 LOOP Pode repetir em funo de uma consulta END LOOP; (select)

FOR target IN query LOOP statements END LOOP;

Nome do projeto, horas e custo (mas pode no haver empregados no projeto)


CREATE TYPE tPCustH AS (nomeproj VARCHAR(20), qthoras FLOAT, custp FLOAT); CREATE OR REPLACE FUNCTION pPCustH(dep int) RETURNS SETOF tPCustH AS $$ DECLARE res tPCustH%rowtype; reg RECORD; BEGIN FOR reg IN SELECT nomeproj, qthoras, custoproj FROM Projeto NATURAL JOIN ProjetoCusto WHERE iddep = dep LOOP res := reg; IF (reg.qthoras IS NULL) THEN res.qthoras:=0; END IF; IF (reg.custoproj IS NULL) THEN res.custp:=0; END IF; RETURN NEXT res; END LOOP; RETURN; END; $$ LANGUAGE plpgsql;

Calcula a idade (data de nasc.)


CREATE OR REPLACE FUNCTION idade(dt DATE) RETURNS INT AS $$ DECLARE mes INT; ano INT; dia INT; BEGIN dia := extract(day from dt); mes := extract(month from dt); ano := extract(year from dt); if mes > extract(month from CURRENT_DATE) then ano := ano + 1; else if mes = extract(month from CURRENT_DATE) then if dia > extract(day from CURRENT_DATE) then ano := ano + 1; end if; end if; end if; RETURN extract(year from CURRENT_DATE) - ano; END; $$ LANGUAGE plpgsql;

Gatilhos

Gatilhos

Gatilho (trigger) condio / ao (tomada caso a condio satisfeita) Gatilhos Podem


Mudar o estado de um objeto (tupla) Completar dados omissos / incorretos Verificar restries semnticas

Regras de Negcio

TODO GATILHO EXECUTA O CDIGO DE UMA FUNO

Disparando Gatilhos

Quando ocorre modificao no estado de uma tabela

INSERT, DELETE ou UPDATE BEFORE ou AFTER NEW - contm os valores da nova linha (record) OLD - contm os valores da linha antiga (record)

Antes ou depois da operao

Variveis de contexto disponveis:


NEW.coluna / OLD.coluna

Um exemplo

A tabela departamento
nomedep iddep idchefe VARCHAR(15) NOT NULL UNIQUE, SERIAL INTEGER NOT NULL PRIMARY KEY, DEFAULT 101 NOT NULL,

CREATE TABLE Departamento (

dtinchef DATE );

Restries

idchefe chave estrangeira (de empregado) No pode haver salrio maior que o do chefe iddep deve ser gerado automaticamente

Criando restries

idchefe chave estrangeira

ALTER TABLE Departamento ADD CONSTRAINT departamento_idchefe_fkey FOREIGN KEY (idchefe) REFERENCES empregado (idemp);

Para gerar iddep usaremos uma sequencia

CREATE SEQUENCE departamento_iddep_seq INCREMENT 1 MINVALUE 1;

E poderemos gerar a chave com o comando


NEW.iddep := nextval('departamento_iddep_seq');

Criando um gatilho
CREATE FUNCTION insDepto() RETURNS trigger AS $$ BEGIN IF (SELECT salario FROM Empregado WHERE idemp = NEW.idchefe) <= ANY (SELECT salario FROM Empregado WHERE iddep = NEW.iddep and idemp <> NEW.idchefe) THEN RAISE EXCEPTION 'No pode ter salario maior ou igual ao de %', NEW.idchefe; -- NO FAZ SENTIDO!!! ELSE NEW.dtinchef:= CURRENT_DATE; END IF; NEW.iddep := nextval('departamento_iddep_seq'); RETURN NEW; END; $$ LANGUAGE plpgsql;

Criando um gatilho
CREATE TRIGGER tbiDepartamento BEFORE INSERT ON Departamento FOR EACH ROW EXECUTE PROCEDURE insDepto(); Testando insert into Departamento values ('Producao', 3, 104, '03-MAR-2008'); insert into Departamento values ('Promocao', 3, 104, '03-MAR-2008'); insert into Departamento(nomedep) values ('Procissao');

Mais gatilhos

Restries

Um dependente que seja filho(s) tem que ter menos de 14 anos Um departamento pode ficar em no mximo trs locais diferentes Um projeto no pode ter mais do que 100 horas no total No se pode mudar um empregado ligado a um projeto (h que exclu-lo de um e inclu-lo em outro) No se pode mudar o ID de departamentos ou de empregados Funcionrios no podem ganhar mais que seus chefes

Um dependente que seja filho(s) no pode ter mais que 14 anos


CREATE FUNCTION insDepen() RETURNS trigger AS $$ BEGIN IF idade(NEW.depdtnasc) >= 14 AND NEW.tipo LIKE 'filh%' THEN RAISE EXCEPTION 'Se %, que seja menor que 14 anos: %', NEW.tipo, NEW.depdtnasc; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql;

Um dependente que seja filho(s) tem que ter menos de 14 anos


CREATE FUNCTION insDepen() RETURNS trigger AS $$ BEGIN IF idade(NEW.depdtnasc) >= 14 AND NEW.tipo LIKE 'filh%' THEN RAISE EXCEPTION 'Se %, que seja menor que 14 anos: %', Esta funo NEW.tipo, NEW.depdtnasc; tambm foi END IF; Definida em RETURN NEW; plpgSQL END; $$ LANGUAGE plpgsql;

Gatilho da tabela dependente


CREATE TRIGGER tbiDependente BEFORE INSERT OR UPDATE ON Dependente FOR EACH ROW EXECUTE PROCEDURE insDepen(); Teste: INSERT INTO Dependente VALUES (113, 'Luana', 'F', '05-APR-1996', 'filha'); ERRO: Se filha, que seja menor que 14 anos: 1996-0405 INSERT INTO Dependente VALUES (114, 'Paulo', 'M', '15-SEP-1996', 'filho'); ERRO: Se filho, que seja menor que 14 anos: 1996-0915 INSERT INTO Dependente VALUES (113, 'Luana', 'F', '05-JUN-2008', 'filha'); INSERT INTO Dependente VALUES (114, 'Paulo', 'M', '15-SEP-2009', 'filho');

Um departamento pode ficar em no mximo trs locais diferentes


CREATE FUNCTION insLocalDepto() RETURNS trigger AS $$ -- nmero de locais atingiu o mximo? BEGIN IF (SELECT COUNT(*) FROM LocalDepto WHERE iddep = NEW.iddep) = 3 THEN RAISE EXCEPTION 'Insercao nao permitida: Excede maximo de locais'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql;

Gatilho da tabela localdepto


CREATE TRIGGER tbiLocalDepto BEFORE INSERT OR UPDATE ON LocalDepto FOR EACH ROW EXECUTE PROCEDURE insLocalDepto(); Testando INSERT INTO LOCALDEPTO VALUES (5, 'Itabuna'); --tenta inserir o 4 local ERRO: Insercao nao permitida: Excede maximo de locais INSERT INTO --insere o INSERT INTO 'Itabuna'); LOCALDEPTO VALUES (2, 'Ilheus'); 3 LOCALDEPTO VALUES (3, --insere o 2

Um projeto no pode ter mais do que 100 horas no total/ No se pode mudar um empregado ligado a um projeto (h que exclu-lo de um e inclu-lo em outro)
CREATE FUNCTION updTrabalhaEm() RETURNS trigger AS $$ BEGIN IF (SELECT SUM(horas)+ NEW.horas-OLD.horas FROM TrabalhaEm WHERE idproj = NEW.idproj) > 100 THEN RAISE EXCEPTION 'Atualizacao nao permitida: Excede maximo de horas'; END IF; IF (NEW.idemp <> OLD.idemp OR NEW.idproj <> OLD.idproj) THEN RAISE EXCEPTION 'Ids no podem ser modificados! Rever operao!'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql;

Gatilho na tabela TrabalhaEm


CREATE TRIGGER tbuTrabalhaEm BEFORE UPDATE ON TrabalhaEm FOR EACH ROW EXECUTE PROCEDURE updTrabalhaEm(); Testando UPDATE TrabalhaEm set idemp =115 where idemp = 102 and idproj = 13; ERRO: Id de Empregado ou Projeto no podem ser modificados! Rever operao! UPDATE TrabalhaEm set horas = 15 where idemp = 102 and idproj = 13; ERRO: Atualizacao nao permitida: Excede maximo de horas UPDATE TrabalhaEm set horas = 4 where idemp = 101 and idproj = 16; UPDATE TrabalhaEm set horas = 11 where idemp = 113 and idproj = 13;

No se pode mudar o ID de departamentos ou de empregados / funcionrios no podem ganhar mais que seus chefes
CREATE FUNCTION updDepto() RETURNS trigger AS $$ BEGIN IF (SELECT salario FROM Empregado WHERE idemp = NEW.idchefe)<= ANY (SELECT salario FROM Empregado WHERE iddep = NEW.iddep and idemp <> NEW.idchefe) THEN RAISE EXCEPTION 'Tem chefiado com salario maior ou igual ao de %', NEW.idchefe; ELSE NEW.dtinchef:= CURRENT_DATE; END IF; IF (NEW.nomedep <> OLD.nomedep OR NEW.iddep <> OLD.iddep) THEN RAISE EXCEPTION 'Nome ou Id no podem ser modificados! Rever operao!'; END IF; RETURN NEW; END; $$ LANGUAGE plpgsql;

Gatilho na tabela Departamento


CREATE TRIGGER tbuDepartamento BEFORE UPDATE ON Departamento FOR EACH ROW EXECUTE PROCEDURE updDepto(); Testando UPDATE Departamento set nomedep = 'Sei lah' where iddep = 3; ERRO: Nome ou Id no podem ser modificados! Rever operao! SQL state: P0001 UPDATE Departamento set idchefe = 113 where iddep = 3; ERRO: Tem chefiado com salario maior ou igual ao de 113 SQL state: P0001 UPDATE Departamento set idchefe = 114 where iddep

Gatilho para gerar chaves de empregado


CREATE SEQUENCE empregado_idemp_seq; CREATE FUNCTION insEmpregado() RETURNS trigger AS $$ BEGIN NEW.idemp = select nextval('empregado_idemp_seq'); END; $$ LANGUAGE plpgsql; CREATE TRIGGER tbiEmpregado BEFORE UPDATE ON empregado FOR EACH ROW EXECUTE PROCEDURE insEmpregado();

Gatilho para gerar chaves de projeto


CREATE SEQUENCE projeto_idproj_seq; CREATE FUNCTION insProjeto() RETURNS trigger AS $$ BEGIN NEW.idproj = select nextval('projeto_idproj_seq;'); END; $$ LANGUAGE plpgsql; CREATE TRIGGER tbiProjeto BEFORE UPDATE ON empregado FOR EACH ROW EXECUTE PROCEDURE insProjeto();

Programao em SGBD

"A verso orientada a objeto do 'cdigo espaguete' o 'cdigo lasanha' - camadas em excesso " Roberto Waltman

Você também pode gostar