Professional Documents
Culture Documents
Apresentao da Disciplina
Ementa:
PL/SQL: blocos annimos, cursores, stored procedures, functions, triggers.
Critrios de Aproveitamento:
Provas: peso 8.0 (oito) Trabalhos: peso 2.0 (dois)
Bibliografia
Livro Texto: 1. Introduo a Sistemas de Banco de Dados; Date, C. J.; Ed. Campus; 8 edio; 2004; 15 exemplares. 2. Sistemas de Banco de Dados; Korth, Henry F.; Ed. Campus; 5 edio; 2006; 15 exemplares. 3. Oracle Database 11g PL/SQL: Programao; McLaughlin, Michel; Ed. Novatec; 1 edio; 2009; 10 exemplares. Complementar: 4. Projeto e Modelagem de Banco de Dados; Lightstone, Teorey; Ed. Campus; 1 edio; 2006; 10 exemplares. 5. Oracle 10g PL/SQL; Oliveira, Celso H. Poderoso de; Ed. Novatec; 1 edio; 2005; 10 exemplares. 6. Oracle 10g Database: Guia do DBA; Serson, Roberto Rubinstein; Ed. Novatec; 1 edio; 2004; 10 exemplares. 7. Oracle 10g: Ideal Para Quem Deseja Iniciar o Aprendizado do Oracle; Ramalho, Jos A.; Ed. Thomson; 1 edio; 2005; 10 exemplares. 8. Gerenciando Banco de Dados: A Abordagem Entidade-Relacionamento para Projeto Lgico; Chen, Peter; Makron Books; 1 edio; 1990; 7 exemplares.
Linguagem PL/SQL
Oracle 11g
Instalao e Servios
No Windows, possvel realizar a instalao default, sem alterar as configuraes padres. Mas fique atento para anotar a senha inicial que solicitada ao usurio SYSTEM (Administrador). Se voc possui um computador com 512Mb de memria (o ideal 1Gb) e processador acima de 2GHertz, pode instalar a verso completa do Oracle 11g. O disco de instalao tem cerca de 600Mb. Para mquinas mais antigas, recomenda-se o uso do Oracle 11g Express Edition. A configurao mnima 256Mb e 1GHertz. O instalador tem cerca de 250Mb.
Obs.: Somente a verso 11g Express Edition gratuita para uso comercial (limitado a 1 base de dados de 4Gb).
6
Instalao e Servios
(cont.)
Afim de carregar o Oracle somente quando realmente desejar utiliz-lo, coloque os 3 servios listados a seguir para serem iniciados manualmente.
ORACLESERVICE - o principal servio. Efetivamente, esse o SGBD e consome bastante recursos do sistema. TNSLISTERNER responsvel por fazer a comunicao entre as chamadas (consultas) ao B.D. e o servio do SGBD. ISQLPLUS o servio que disponibiliza o programa iSQLPlus para realizar a conexo ao Oracle atravs de browser (11g).
Para localizar os servios, que na instalao so colocados para iniciar automaticamente, procure em:
INICIAR / CONFIGURAES / PAINEL DE CONTROLE / FERRAMENTAS ADMINISTRATIVAS / SERVIOS
iSQLPlus
http://<servidor>:<porta>/isqlplus http://ANDROMEDA:5560/isqlplus
(cont.)
Criando um usurio chamado wagner cuja senha unilins: CREATE USER wagner IDENTIFIED BY unilins; Concedendo privilgio de Administrador (DBA) ao usurio: GRANT DBA TO wagner; Conectando-se com o novo usurio: CONN WAGNER/UNILINS@ORCL; Para verificar o usurio atual: SHOW USER;
Obs.: Por questo de segurana, no recomendado atribuir o privilgio de DBA para usurios comuns do sistemas.
10
12
Bloco Annimo
So blocos PL/SQL que podem ser gerados para executar uma sequencia lgica de instrues SQL e procedurais. Prottipo bsico:
[DECLARE] seo declarativa variveis, tipos, cursores e subprogramas BEGIN seo executvel instrues SQL e procedurais [EXCEPTION] seo de tratamento de excees END;
possvel, por exemplo, criar um bloco annimo para realizar a manuteno em algumas tabelas. Se esse bloco for salvo em arquivo SCRIPT, possvel carrega-lo e executa-lo todas as vezes que for necessrio.
13
SET SERVEROUTPUT on; DECLARE N number := 5; BEGIN dbms_output.put_line('Valor de N ' || N); END;
SERVEROUTPUT serve para habilitar a sada e fazer testes. || serve para concatenao
14
SET SERVEROUTPUT on; DECLARE S varchar2(20); BEGIN S := '&input'; dbms_output.put_line('Valor de S ' || S); END;
&input uma varivel de substituio, usada para permitir que o usurio informe valores.
15
Comentrios
H 2 tipos de comentrios em PL/SQL:
-- comentrio de uma nica linha. /* comentrio de mltiplas linhas. em geral, usado para textos extensos. */
17
Os statements CASE tambm so blocos e terminam com END CASE. A seguir, veremos exemplos do uso de IF e CASE.
18
BEGIN IF 1>2 THEN dbms_output.put_line('Major'); ELSIF 1<2 dbms_output.put_line('Minor'); ELSE dbms_output.put_line('Equal'); END IF; END;
20
Operadores Lgicos
BEGIN IF 1=1 AND 2=2 THEN dbms_output.put_line('True'); END IF; END;
Operadores Lgicos
(cont.)
BEGIN IF 1 BETWEEN 1 AND 3 THEN dbms_output.put_line('In the range'); END IF; END;
22
Operadores Lgicos
(cont.)
23
Operadores Lgicos
(cont.)
DECLARE var BOOLEAN; BEGIN IF var IS NULL THEN dbms_output.put_line('It is null'); END IF; END;
24
ELSE
dbms_output.put_line('Nenhuma das alternativas');
Estruturas Iterativas
O PL/SQL permite loops FOR, SIMPLE e WHILE. Loops FOR podem ser numricos e cursor.
O loop numrico repete atravs de um valor definido, enquanto loop cursor repete atravs de registros retornados por um cursor statement SELECT (veremos posteriormente).
Loops simples so estruturas explcitas. Eles exigem que voc gerencie ambos os ndice loop e o critrio exit. O loop WHILE difere do loop simples, pois controla a entrada para o loop, no a sada. Vermos a seguir os exemplos de estruturas iterativas.
27
28
30
Agora que j conhecemos as principais estruturas de controle da linguagem PL/SQL, iremos ver como usar em conjunto dos recursos SQL. Assim, alm dos comando DML (SELECT, INSERT, UPDATE, DELETE), poderemos declarar variveis e usar instrues de controle (IF, CASE, FOR, LOOP, WHILE).
31
DECLARE qtde number; BEGIN SELECT count(*) INTO qtde FROM tb_teste; dbms_output.put_line(' Existem ' || qtde || ' registros.'); END;
32
BEGIN FOR REG IN (SELECT descricao FROM tb_teste) LOOP dbms_output.put_line(REG.descricao); END LOOP; END;
A varivel REG instanciada automaticamente e o lao FOR executado de acordo com a quantidade de registros retornados pela consulta.
33
BEGIN FOR I IN 4..10 LOOP INSERT INTO tb_teste (codigo, descricao) VALUES (seq_teste.NEXTVAL, 'Teste ' || I); END LOOP; END;
34
BEGIN FOR REG IN (SELECT codigo FROM tb_teste) LOOP IF MOD(REG.codigo, 2) = 0 then DELETE FROM tb_teste WHERE REG.codigo = codigo; END IF; END LOOP; END;
ps.
Utilize ROLLBACK para desfazer a excluso. lembre-se, o ROLLBACK s ir funcionar at o ltimo COMMIT.
36
Exemplo 14:
Apresentar cdigo e descrio de todos os registros cadastrados. Ao final, apresentar a quantidade de registros listados. Caso a tabela esteja vazia,emitir uma mensagem de aviso.
BEGIN SELECT count(*) INTO qtde FROM tb_teste; IF qtde > 0 then FOR REG IN (select * from tb_teste) LOOP
dbms_output.put_line(REG.codigo||' - '|| REG.descricao);
end LOOP;
dbms_output.put_line('Listados: ' || qtde);
else
dbms_output.put_line('No h registros');
37
Exemplo 15:
Apresentar em maisculo a descrio dos registros com cdigo at 5 e em minsculo a descrio dos registros com cdigo acima de 5.
BEGIN FOR REG IN (select * from tb_teste) LOOP IF REG.codigo <= 5 THEN REG.descricao := lower(REG.descricao); ELSE REG.descricao := upper(REG.descricao); END IF; dbms_output.put_line(REG.codigo||' - '|| REG.descricao); end LOOP; END;
38
Exemplo 16:
Apresentar todas as descries em minsculo, exceto a do registro com o maior cdigo de dever aparecer em maisculo.
DECLARE maior Number; BEGIN SELECT MAX(codigo) INTO maior FROM tb_teste; FOR REG IN (select * from tb_teste) LOOP IF REG.codigo <> maior THEN REG.descricao := lower(REG.descricao); ELSE REG.descricao := upper(REG.descricao); END IF; dbms_output.put_line(REG.codigo||' - '|| REG.descricao); end LOOP; END;
39
Exerccios
1. 2. Criar um bloco annimo que compare 2 nmeros e apresente o maior valor. Altere o exerccio anterior para que seja prevista a possibilidade dos valores serem iguais. 3. Altere o exerccio anterior para que seja prevista a possibilidade de existir valor nulo. 4. Criar um bloco annimo que dado o nmero de minutos retorne o nmero de horas correspondente. 5. Crie uma funo que dado um salrio retorne o nmero de salrios mnimos que a pessoa ganha. 6. Fazer um bloco annimo para verificar se o ano atual par. 7. Fazer um bloco annimo para verificar se o ano atual bissexto. 8. Criar um bloco annimo para apresentar os anos bissextos entre 2000 e 2100. 9. Escreva um bloco annimo que crie uma varivel do tipo date, atribua a data corrente e mostre na tela: A data atual e o ltimo dia deste ms. 10. Criar um bloco PL/SQL para imprimir os 15 primeiros termos da sequncia de Fibonacci: 1 1 2 3 5 8 13 21 34 55 ...
40
41
2. Altere o exerccio anterior para que seja prevista a possibilidade dos valores serem iguais.
set serveroutput on; declare n1 number := 3; n2 number := 5; begin if (n1>n2) then dbms_output.put_line(n1 || ' maior'); elsif (n1<n2) then dbms_output.put_line(n2 || ' maior'); else dbms_output.put_line('Valores iguais'); end if; end;
42
3. Altere o exerccio anterior para que seja prevista a possibilidade de existir valor nulo.
set serveroutput on; declare n1 number := 3; n2 number := 5; begin if (n1 is null) or (n2 is null) then dbms_output.put_line('Valores invlidos!'); elsif (n1>n2) then dbms_output.put_line(n1 || ' maior'); elsif (n1<n2) then dbms_output.put_line(n2 || ' maior'); else dbms_output.put_line('Valores iguais'); end if; end;
43
4. Criar um bloco annimo que dado o nmero de minutos retorne o nmero de horas correspondente.
set serveroutput on; declare M number := 120; H number; begin H := M / 60; dbms_output.put_line(H); end;
44
5. Crie uma funo que dado um salrio retorne o nmero de salrios mnimos que a pessoa ganha.
set serveroutput on; declare minimo number := 622.00; salario number; qtde: number; begin salario := &salario; qtde := salario / minimo; dbms_output.put_line(qtde ' salrios mnimos'); end;
45
46
47
8. Criar um bloco annimo para apresentar os anos bissextos entre 2000 e 2100.
set serveroutput on; declare ano number; begin dbms_output.put_line('Anos bissextos entre 2000 e 2100'); for ano in 2000..2100 loop if ano mod 4 = 0 then dbms_output.put_line(ano); end if; end loop; end;
48
9. Escreva um bloco annimo que crie uma varivel do tipo date, atribua a data corrente e mostre na tela: A data atual e o ltimo dia deste ms.
set serveroutput on; declare hoje date; ultimo date; begin select sysdate, last_day(sysdate) into hoje, ultimo from dual; dbms_output.put_line(hoje || ' | ' || ultimo); end;
49
10. Criar um bloco PL/SQL para imprimir os 15 primeiros termos da sequncia de Fibonacci: 1 1 2 3 5 8 13 21 34 55 ...
set serveroutput on; declare ult number := 1; ant number := 0; aux number; begin for i in 1..15 loop dbms_output.put_line(ult); aux := ult; ult := ult + ant; ant := aux; end loop; end;
50
Cursores
Um cursor um tipo abstrato de dados da linguagem PL/SQL que permite armazenar o resultado de uma instruo SELECT. Ou seja, um cursor uma matriz gerada dinamicamente a partir de uma consulta. Existem 2 tipos de cursores: explcitos e implcitos.
Cursor explcito criado no bloco de declaraes. Cursor implcito criado dentro de qualquer bloco de execuo ou de exceo.
52
Declare cursor C is select descricao, preco from tb_produto; Begin For R in C loop dbms_output.put_line(R.descricao || ' ' || R.preco); End loop; End;
O cursor foi criado de forma implcita. No exemplo, o cursor aberto e fechado automaticamente. O lao de repetio tambm incrementado de forma automtica.
55
Declare cursor C is select p.descricao, p.preco, c.descricao categoria from tb_produto p inner join tb_categoria c on p.cod_cat = c.cod_cat; Begin For R in C loop dbms_output.put_line(R.descricao || ' ' || R.preco || ' ' || R.categoria); End loop; End;
ps.
56
Apresentar uma listagem de cada categoria com seus respectivos produtos utilizando quebra de sesso por categoria.
CATEGORIA HIGIENE:
57
(cont.)
Begin For RC in CC loop dbms_output.put_line('Categoria: ' || RC.descricao); For RP in CP(RC.cod_cat) loop dbms_output.put_line('- ' || RP.descricao || 'R$ ' || RP.preco); End loop; End loop; End;
58
Stored Procedure
Um Stored Procedure um tipo de programa PL/SQL que, aps ser compilado, permanece armazenado no servidor do banco de dados, podendo ser executado a qualquer momento diretamente no banco ou atravs de uma linguagem de programao. possvel criar Stored Procedures com ou sem parmetros formais. A execuo de uma stored procedure mais rpida do que a execuo de instrues SQL simples. Sintaxe: CREATE [OR REPLACE] PROCEDURE <nome_procedimento> AS <declaracao_variaveis> BEGIN <bloco_instrues> END;
59
CREATE OR REPLACE procedure prc_inserir_produto( p_desc varchar, p_preco number) AS BEGIN INSERT INTO tb_produto(cod_prod, descricao, preco) VALUES(sq_produto.nextval, p_desc, p_preco); COMMIT; END;
Exemplo 2: Modificando o exemplo anterior para gravar a descrio em maisculo e sem espaos.
CREATE OR REPLACE procedure prc_inserir_produto( p_desc varchar, p_preco number) AS BEGIN INSERT INTO tb_produto(cod_prod, descricao, preco) VALUES(sq_produto.nextval,
upper(trim(p_desc)),
p_preco);
COMMIT; END;
61
Exemplo 3: Modificando o exemplo anterior para cadastrar somente valor positivo para o preo. CREATE OR REPLACE procedure prc_inserir_produto( p_desc varchar, p_preco number) AS v_preco number; BEGIN if p_preco < 0 then v_preco := p_preco * (-1); else v_preco := p_preco; end if; INSERT INTO tb_produto(cod_prod, descricao, preco) VALUES(sq_produto.nextval, p_desc, v_preco); COMMIT; END;
62
create or replace procedure prc_par_impar (p_t IN number, p_ret OUT varchar2) is begin if(MOD(p_t,2)=0) then p_ret := ' par'; else p_ret := ' mpar'; end if; end;
63
Exerccios
(1/3)
1. Criar um stored procedure que receba como parmetro o cdigo de uma categoria e apresente todos os produtos desta categoria. 2. Modificar o programa anterior de forma que seja recebida a descrio da categoria ao invs do cdigo. 3. Elaborar um stored procedure que receba como parmetro a descrio de uma categoria e atualize os preos dos produtos. Os produtos com preo at 100 reais sero reajustados em 3%, os demais tero aumento de 5%. 4. Criar uma stored procedure para apresentar a descrio de cada categoria com a respectiva quantidade de produtos cadastrados. 5. Criar uma stored procedure para apresentar a descrio, o preo e a categoria do produto mais caro.
64
Exerccios
(2/3)
6. Criar um stored procedure para receber a descrio de uma categoria e o percentual de aumento que dever ser aplicado a todos os produtos. 7. Criar um stored procedure para receber 2 cdigos de produtos. Deve-se aumentar em 8% o produto mais barato e baixar 6% o produto mais caro. 8. Criar um stored procedure para listar todos os produtos com quebra de sesso por categoria. 9. Criar um stored procedure para apagar as categorias que no possuem nenhum produto. 10. Criar um stored procedure para listar os produtos cadastrados em duplicidade (pela descrio).
65
Exerccios
(3/3)
11. Criar um stored procedure para listar os produtos at um determinado preo. O valor do limite dever ser passado como parmetro. 12. Criar um stored procedure para listar os produtos cujo preo esteja dentro de um intervalo. Os limites do intervalo devero ser passados como parmetros. 13. Criar um stored procedure para listar as categorias com mais de 5 produtos. 14. Criar um stored procedure para listar todos os produtos com quebra de sesso por categoria, somente das categorias com mais de 5 produtos. 15. Criar um stored procedure que receba o cdigo de uma categoria e faa a excluso de seu registro, mas somente se a categoria no tiver produtos cadastrados.
66
1.
Criar um stored procedure que receba como parmetro o cdigo de uma categoria e apresente todos os produtos desta categoria.
create or replace procedure prc_mostrar_produtos1(x number) AS cursor p(y number) is select * from tb_produto where cod_cat = y; begin for r in p(x) loop dbms_output.put_line('Descricao: '||r.descricao); dbms_output.put_line('Preco: ' || r.preco); end loop; end; exec prc_mostrar_produtos1(1);
67
2.
Modificar o programa anterior de forma que seja recebida a descrio da categoria ao invs do cdigo.
create or replace procedure prc_mostrar_produtos2(x varchar) AS cursor p(y varchar) is select p.descricao, p.preco from tb_produto p inner join tb_categoria c on p.cod_cat = c.cod_cat where c.descricao = y; begin for r in p(x) loop dbms_output.put_line('Descricao: '||r.descricao); dbms_output.put_line('Preco: ' || r.preco); end loop; end; exec prc_mostrar_produtos2('Informtica');
68
3.
Elaborar um stored procedure que receba como parmetro a descrio de uma categoria e atualize os preos dos produtos. Os produtos com preo at 100 reais sero reajustados em 3%, os demais tero aumento de 5%.
create or replace procedure prc_atualizar_produtos(x varchar) as v_cod_cat number; begin select cod_cat into v_cod_cat from tb_categoria where descricao = x; update tb_produto set preco = preco * 1.03 where cod_cat = v_cod_cat and preco <= 100; update tb_produto set preco = preco * 1.05 where cod_cat = v_cod_cat and preco > 100; end; exec prc_atualizar_produtos('Informtica');
69
4.
Criar uma stored procedure para apresentar a descrio de cada categoria com a respectiva quantidade de produtos cadastrados.
create or replace procedure prc_listar_categorias as cursor c is select c.descricao, count(*) qtde from tb_categoria c inner join tb_produto p on c.cod_cat = p.cod_cat group by c.descricao; begin for r in c loop dbms_output.put_line(r.descricao || ' - ' || r.qtde); end loop; end;
70
5.
Criar uma stored procedure para apresentar a descrio, o preo e a categoria do produto mais caro.
create or replace procedure prc_mais_caro as v_max number; cursor b(p_preco) is select p.descricao, p.preco, c.descricao categoria from tb_categoria c left join tb_produto p on c.cod_cat = p.cod_cat where p.preco = p_preco; begin select MAX(preco) into v_max from tb_produto; for r in b loop dbms_output.put_line(r.descricao || ' - ' || r.preco || ' - ' || r.categoria); end loop; end; exec prc_mais_caro;
71
6.
Criar um stored procedure para receber a descrio de uma categoria e o percentual de aumento que dever ser aplicado a todos os produtos.
create or replace procedure prc_categoria_aumento(p_cat varchar, p_aumento number) as begin update tb_produto set preco = preco + (preco * p_aumento / 100) where cod_cat = (select cod_cat from tb_categoria where descricao = p_cat); end; exec prc_categoria_aumento('Informtica', 3);
72
7.
Criar um stored procedure para receber 2 cdigos de produtos. Deve-se aumentar em 8% o produto mais barato e baixar 6% o produto mais caro.
create or replace procedure prc_produto_aumento(p_cod1 number, p_cod2 number) as preco1 number; preco2 number; begin select preco into preco1 from tb_produto where cod_prod = p_cod1; select preco into preco2 from tb_produto where cod_prod = p_cod2; if preco1<preco2 then update tb_produto set preco=preco*1.08 where cod_prod=p_cod1; update tb_produto set preco=preco*0.94 where cod_prod=p_cod2; else update tb_produto set preco=preco*0.94 where cod_prod=p_cod1; update tb_produto set preco=preco*1.08 where cod_prod=p_cod2; end if; end;
73
8.
Criar um stored procedure para listar todos os produtos com quebra de sesso por categoria.
create or replace procedure prc_produtos_categoria as cursor a is select cod_cat, descricao from tb_categoria; cursor b(p_cod_cat) is select descricao, preco from tb_produto where cod_cat = p_cod_cat; begin for c in a loop dbms_output.put_line(c.descricao); for d in b(c.cod_cat) loop dbms_output.put_line(d.descricao || ' ' || d.preco); end loop; end loop; end; exec prc_produtos_categoria;
74
9.
Criar um stored procedure para apagar as categorias que no possuem nenhum produto.
create or replace procedure prc_categoria_vazia as begin delete from tb_categoria where cod_cat in (select c.cod_cat from tb_categoria c where not exists (select 1 from tb_produto p where p.cod_cat=c.cod_cat)); end;
75
10. Criar um stored procedure para listar os produtos cadastrados em duplicidade (pela descrio).
create or replace procedure prc_produtos_duplicados as cursor a is select descricao, count(*) qtde from tb_produto group by descricao having count(*) > 1; begin dbms_output.put_line('Produtos com descricao duplicada: '); for r in a loop dbms_output.put_line(r.descricao || ' - ' || r.qtde); end loop; end; exec prc_produtos_duplicados;
76
11. Criar um stored procedure para listar os produtos at um determinado preo. O valor do limite dever ser passado como parmetro. create or replace procedure prc_produto_preco(p_max number)as cursor a(p_maximo number) is select descricao, preco from tb_produto where preco <= p_maximo; begin for r in a(p_max) loop dbms_output.put_line(r.descricao || ' - ' || r.preco); end loop; end; exec prc_produto_preco(100);
77
12. Criar um stored procedure para listar os produtos cujo preo esteja dentro de um intervalo. Os limites do intervalo devero ser passados como parmetros. create or replace procedure prc_produto_faixa_preco(p_min number, p_max number)as cursor a(p_minimo number, p_maximo number) is select descricao, preco from tb_produto where preco between p_minimo and p_maximo; begin for r in a(p_min, p_max) loop dbms_output.put_line(r.descricao || ' - ' || r.preco); end loop; end; exec prc_produto_faixa_preco(80,120);
78
13. Criar um stored procedure para listar as categorias com mais de 5 produtos.
create or replace procedure prc_listar_categorias as cursor c is select c.descricao, count(*) qtde from tb_categoria c inner join tb_produto p on c.cod_cat = p.cod_cat group by c.descricao having count(*) > 5; begin for r in c loop dbms_output.put_line(r.descricao || ' - ' || r.qtde); end loop; end;
79
14. Criar um stored procedure para listar todos os produtos com quebra de sesso por categoria, somente das categorias com mais de 5 produtos. create or replace procedure prc_produtos_categoria_5 as cursor a is select c.cod_cat, c.descricao, count(*) qtde from tb_categoria c inner join tb_produto p on c.cod_cat = p.cod_cat group by c.cod_cat, c.descricao having count(*) > 5; cursor b(p_cod_cat number) is select descricao, preco from tb_produto where cod_cat = p_cod_cat; begin for c in a loop dbms_output.put_line(c.descricao); for d in b(c.cod_cat) loop dbms_output.put_line(d.descricao || ' ' d.preco); end loop; end loop; end;
80
15. Criar um stored procedure que receba o cdigo de uma categoria e faa a excluso de seu registro, mas somente se a categoria no tiver produtos cadastrados. create or replace procedure prc_excluir_categoria(p_cod_cat number) as v_qtde number; cursor a is select COUNT(*) into v_qtde from tb_produto where cod_cat = p_cod_cat; begin if v_qtde = 0 then delete from tb_categoria where cod_cat = p_cod_cat; dbms_output.put_line('Excluso realizada'); else dbms_output.put_line('Excluso no realizada'); end if; end; exec prc_excluir_categoria(1);
81
Tipos de Exceo
Os erros de programas PL/SQL ocorrem quando voc comete um erro digitando o programa ou definindo o programa.
Erros de compilao Erros de execuo (run-time)
82
Erros de compilao
So geralmente erros de digitao:
Esquecer um ponto e vrgula No fechar uma string literal Escrever de maneira errada um identificador (palavra reservada) Comentar um valor lxico requerido pelas regras de anlise
comum que um erro de compilao seja acusado na linha seguinte em que tenha ocorrido.
83
SQLERRM
(1/2)
ERRO
ORA-06530 ORA-06592 ORA-06531 ORA-00001 ORA-01001 ORA-01722 ORA-01017 ORA-01403 ORA-01012
QUANDO OCORRE
Ocorre ao tentar acessar um objeto no inicializado. Ocorre quando um statement CASE criado sem uma clusula ELSE e nenhuma condio executada. Ocorre ao tentar abrir um cursor que j est aberto. Ocorre ao tentar inserir um valor duplicado de ndice. Ocorre ao tentar uma operao no permitida sobre um cursor, como fechar um cursor fechado. Ocorre ao tentar atribuir algo no numrico para um campo numrico. Ocorre ao tentar entrar em um programa com um nome de usurio ou senha invlida. Ocorre ao tentar usar a estrutura SELECT-INTO e o statement retornar um valor nulo. Ocorre quando um programa lanar uma chamada de banco de dados e no estiver conectado, por exemplo aps ter desconectado sua sesso.
85
(2/2)
ERRO
ORA-06501 ORA-06504 ORA-06500 ORA-01410 ORA-00051 ORA-01422 ORA-0650 ORA-01476
QUANDO OCORRE
Ocorre quando o Oracle no tenha pego formalmente o erro ocorrido. Ocorre quando sua estrutura de cursor falhar ao concordar com sua varivel cursor ou parmetro formal Esse erro ocorre quando o SGA ficar sem memria ou estiver corrompido. Ocorre ao tentar converter uma string em um valor ROWID invlido. Ocorre quando o BD no for capaz de assegurar um bloqueio a um recurso. Ocorre ao usar o SELECT-INTO ou sub-query e a consulta retornar mais de uma linha. Isso ocorre ao tentar atribuir uma varivel dentro de outra que muito pequena para sustent-la. Isso ocorre ao tentar dividir um nmero por zero.
86
87
(cont.)
DECLARE v_codigo tb_produto.cod_prod%TYPE; v_descricao tb_produto.descricao%TYPE; v_preco tb_produto.preco%TYPE; BEGIN v_codigo := 12; prc_buscaProduto(v_codigo, v_descricao, v_preco); dbms_output.put_line('Cdigo: ' || v_codigo); dbms_output.put_line('Descrio: ' || v_descricao); dbms_output.put_line('Preo: ' || v_preco); END;
90
DECLARE v_descricao VARCHAR(10); v_preco number(3,2); BEGIN v_descricao := 'Pen drive de 128GB e USB3'; v_preco := 144.99; dbms_output.put_line ('Descrio: ' || v_descricao); dbms_output.put_line ('Preo: ' || v_preco); EXCEPTION WHEN VALUE_ERROR THEN dbms_output.put_line (SQLERRM); WHEN Others THEN dbms_output.put_line (SQLERRM); END;
92
DECLARE v_descricao VARCHAR(10); v_preco number(3,2); BEGIN v_descricao := 'Pen drive'; v_preco := 'A49.99'; dbms_output.put_line ('Descrio: ' || v_descricao); dbms_output.put_line ('Preo: ' || v_preco); EXCEPTION WHEN INVALID_NUMBER THEN dbms_output.put_line ('Valor numrico invlido'); WHEN Others THEN dbms_output.put_line (SQLERRM); END;
93
DECLARE valor number(8,2); BEGIN valor := 100 / 0; dbms_output.put_line ('Resultado: ' || valor); EXCEPTION WHEN ZERO_DIVIDE THEN dbms_output.put_line (SQLERRM); WHEN Others THEN dbms_output.put_line (SQLERRM); END;
94
98
PRAGMA EXECEPTION_INIT
O PRAGMA EXCEPTION_INIT associa um nome de exceo a um nmero de erro do Oracle.
Capturar erro -2292, violao de restrio de integridade.
DECLARE e_categoria EXCEPTION; PRAGMA EXCEPTION_INIT (e_categoria, -2292); v_cod_cat tb_categoria.cod_cat%TYPE := &p_cod_cat; BEGIN DELETE FROM tb_categoria WHERE cod_cat = v_cod_cat; COMMIT; EXCEPTION WHEN e_categoria THEN DBMS_OUTPUT.PUT_LINE('Categoria no pode ser removida. Existem produtos relacionados.'); END;
99
RAISE_APPLICATION_ERROR
O RAISE_APPLICATION_ERROR aborta a execuo.
DECLARE sem_comissao EXCEPTION; -declara uma exception BEGIN IF comissao IS NULL THEN RAISE sem_comissao; -chama a exception END IF; bonus := (salario * 0.10) + (comissao * 0.15); EXCEPTION WHEN sem_comissao THEN RAISE_APPLICATION_ERROR(-21000,No h comisso!); END;
100
Exerccios
1. Criar um bloco annimo que receba como parmetro o cdigo de um produto e o percentual de aumento. Deve-se atualizar o preo do produto. Caso o cdigo informado no exista ou o preo atual seja nulo, deve-se gerar uma exceo. Alterar o exerccio anterior trabalhando com stored procedure. Criar um stored procedure que receba o cdigo de um produto a ser excludo. Caso o produto no exista, gerar uma exceo. Criar um stored procedure que receba a descrio, preo e categoria de um produto. Verificar:
a) b) c) d) e) Se Se Se Se Se todos os campos foram preenchidos o tamanho do campo descrio compatvel. o tipo do campo preo vlido. a categoria informada existe. outro erro ocorrer, apresentar o cdigo e a descrio do erro.
2. 3. 4.
101
(1/3)
COTACAO
cod_cot qtde data_inicio data_termino
1..1
0..N
PRODUTO
cod_prod descricao preco estoque
PROPOSTA
valor prazo_entrega
O diagrama de classes acima representa um sistema para cotao de preos, no qual: cada cotao possui um produto; cada cotao recebe propostas de diversos fornecedores; cada fornecedor poder enviar proposta para uma ou mais cotaes.
102
(2/3)
1. Criar um bloco annimo para listas os fornecedores cadastrados. 2. Criar um bloco annimo para listas todas as cotaes cadastradas (com descrio do produto, qtde e perodo) 3. Criar um bloco annimo para listas as cotaes em aberto (com descrio do produto, qtde cotada e perodo). 4. Criar um bloco annimo que apresente a relao de produtos com as respectivas cotaes j realizadas. 5. Criar um stored procedure para cadastrar novos fornecedores. O cdigo do fornecedor dever ser gerado automaticamente e deve-se verificar se o cnpj j est cadastrado.
103
(3/3)
6. Criar um stored procedure que receba o cdigo de uma cotao e apresente as propostas recebidas (valor, prazo, razo social e cnpj do fornecedor) 7. Criar uma stored procedure que receba o cdigo de um produto e apresente as cotaes j realizadas. 8. Criar um stored procedure que receba o cnpj de um fornecedor e apresente as proposta feitas (valor, prazo, produto e qtde) 9. Criar um stored procedure que receba como parmetro uma data e apresente todas as cotaes ativas nesta data. 10. Criar um stored procedure que apresente as cotaes encerradas, abertas ou ainda aguardando incio. Deve-se passar como parmetro qual filtro dever ser aplicado.
104
Function
(funo)
Uma funo um programa PL/SQL que sempre retorna um valor aps sua execuo. Assim como em Stored Procedure, podemos passar parmetros para uma funo. A diferena que as funes so chamadas como parte de uma expresso.
Sintaxe:
CREATE [OR REPLACE] FUNCTION <nome_funcao> [ (<parametros> <IN | OUT> <type>) ] RETURN <type> IS <variaveis> : <type>; BEGIN <bloco_pl_sql>; RETURN <variavel_ou_valor>; END;
105
Com SQL:
SELECT function_name(parameter) FROM table_name;
107
108
110
Exemplo 5: Calcular desconto de 3% se o preo custar at 100 reais, de 5% para at 1000 reais e de 8% se for acima de 1000 reais.
CREATE OR REPLACE FUNCTION fn_promocao(preco number) RETURN number IS promocao number; BEGIN if preco <= 100 then promocao := preco - (preco * 0.03); elsif preco <=1000 then promocao := preco - (preco * 0.05); else promocao := preco - (preco * 0.08); end if; RETURN promocao; END;
111
Exerccios
1. 2. Crie uma funo que receba um nmero e retorne seu valor fatorial. 3! = 1x2x3 Criar uma funo que receba um nome completo e retorne apenas o primeiro nome. 3. Crie uma funo que receba o nmero do ms e retorne o nome deste. 4. Crie uma funo que receba como parmetro uma data (dia, ms e ano) e retorne a data por extenso. Exemplo: 12/03/2008 12 de maro de 2008 5. Criar uma funo que receba uma data e retorne a idade em anos. 6. Criar uma funo para recuperar o nmero de horas e minutos entre duas datas. 7. Criar uma funo que receba um CPF e retorne se vlido. 8. Criar uma funo que receba como parmetro o valor inicial e o valor final e retorne a quantidade de produtos com preo dentro do intervalo. 9. Criar uma funo que receba a descrio de um produto e retorne se o produto est cadastrado ou no. 10. Criar uma funo receber o preo de um produto e o classificar como baixo, mdio ou alto, respectivamente para valores at 100, acima de 100 at 1000 e acima de 1000.
112
2. Criar uma funo que receba um nome completo e retorne apenas o primeiro nome.
CREATE OR REPLACE FUNCTION primeiro_nome(p_nome varchar) RETURN varchar IS v_nome varchar(80); v_posicao number; BEGIN v_posicao := instr(ltrim(p_nome),' '); if v_posicao = 0 then v_nome := p_nome; else v_nome := substr(ltrim(p_nome), 1, v_posicao-1); end if; return v_nome; END; SELECT nome, primeiro_nome(nome) FROM tb_funcionario;
114
END CASE; RETURN v_mes; END; SELECT fn_mes_extenso(8) FROM dual; --Agosto
115
4. Crie uma funo que receba como parmetro uma data (dia, ms e ano) e retorne a data por extenso. Exemplo: 12/03/2008 12 de maro de 2008.
CREATE OR REPLACE FUNCTION fn_data_extenso(p_data date) RETURN varchar IS BEGIN RETURN to_char(p_data, 'DD de MONTH de YYYY', 'nls_date_language=portuguese'); END; SELECT fn_data_extenso(SYSDATE) FROM dual;
-----
maneira de separar a data v_dia := EXTRACT(DAY FROM p_data); v_mes := EXTRACT(MONTH FROM p_data); v_ano := EXTRACT(YEAR FROM p_data);
116
5. Criar uma funo que receba uma data e retorne a idade em anos.
CREATE OR REPLACE FUNCTION fn_idade(p_data date) RETURN number IS v_idade number; BEGIN SELECT floor(months_between(SYSDATE, p_data) / 12) INTO v_idade FROM dual; RETURN v_idade; END; SELECT fn_idade('01/05/2010') FROM dual;
117
6. Criar uma funo para recuperar o nmero de horas e minutos entre duas datas. (1/2)
CREATE OR REPLACE FUNCTION fn_diferenca_datas (p_inicial date, p_final date) IS RETURN varchar; diferenca number; horas number; minutos number; BEGIN SELECT p_final-p_inicial INTO diferenca FROM dual; SELECT trunc(diferenca)*24 + trunc((diferenca - trunc(diferenca))*24), trunc((((diferenca - trunc(diferenca))*24) trunc((diferenca - trunc(diferenca))*24))*60) INTO horas, minutos FROM dual; RETURN horas || 'h' || minutos || 'm'; END;
118
6. Criar uma funo para recuperar o nmero de horas e minutos entre duas datas. (2/2)
Exemplo de uso: DECLARE data_inicial date; data_final date; resposta varchar(20); BEGIN
data_inicial := to_date('21/11/2010 18:57:00','dd/mm/yyyy hh24:mi:ss'); data_final resposta END; := to_date('22/11/2010 21:40:25','dd/mm/yyyy hh24:mi:ss'); := fn_diferenca_datas(data_inicial, data_final);
dbms_output.put_line(resposta);
119
8. Criar uma funo que receba como parmetro o valor inicial e o valor final e retorne a quantidade de produtos com preo dentro do intervalo.
CREATE OR REPLACE FUNCTION fn_qtde_produtos (p_inicial NUMBER, p_final NUMBER) RETURN number IS v_qtde number; BEGIN SELECT COUNT(*) INTO v_qtde FROM tb_produto WHERE preco BETWEEN p_inicial AND p_final; RETURN v_qtde; END; Exemplo de uso: DECLARE v_qtde number(10); BEGIN v_qtde := fn_qtde_produtos(50,80); dbms_output.put_line(v_qtde || ' produtos encontrados!'); END;
122
9. Criar uma funo que receba a descrio de um produto e retorne se o produto est cadastrado ou no.
CREATE OR REPLACE FUNCTION fn_procurar(p_descricao IN varchar) RETURN boolean IS v_qtde number; BEGIN SELECT COUNT(*) INTO v_qtde FROM tb_produto WHERE descricao = p_descricao; RETURN (v_qtde<>0); END;
Exemplo de uso: DECLARE v_descricao varchar(100) := 'Mouse'; BEGIN IF(fn_procurar(v_descricao)) THEN dbms_output.put_line('Produto no encontrado!'); ELSE dbms_output.put_line('Produto encontrado!'); END IF; END;
123
10. Receber o preo de um produto e o classificar como baixo, mdio ou alto, respectivamente para valores at 100, acima de 100 at 1000 e acima de 1000.
CREATE OR REPLACE FUNCTION fn_classificar(p_preco IN number) RETURN varchar IS BEGIN IF p_preco <= 100 THEN RETURN 'baixo'; ELSIF p_preco <= 1000 THEN RETURN 'mdio'; ELSE RETURN 'alto'; END IF; END;
124
Package (Pacote)
Package so reas de armazenamentos de subprogramas (stored procedure e function), constantes, variveis e cursores em PL/SQL que podero ser compartilhados com outros aplicativos. Em outras palavras, os pacotes funcionam como bibliotecas do PL/SQL. Existem packages nativos do Oracle e possvel criar novos pacotes. Um PACKAGE constitudo de duas partes:
PACKAGE SPECIFICATION PACKAGE BODY
125
Partes do Package
PACKAGE SPECIFICATION
Tem como funo de criar a interface (prottipo) de suas aplicaes e definir os tipos de variveis, cursores, excees, nomear rotinas, e funes, tudo que se define na parte de SPECIFICATION do seu PACKAGE poder ser compartilhado com outros scripts ou programas em SQL e PL/SQL.
PACKAGE BODY
Tem como funo executar por completo todas as suas rotinas, cursores e etc., que voc definiu na SPECIFICATION. Alm disso no BODY voc poder criar detalhes e declaraes que ficaro invisveis para outras aplicaes.
126
127
Especificao do Pacote
CREATE OR REPLACE PACKAGE pkg_comissao IS comissao_padrao NUMBER := 0.10; PROCEDURE mudar_comissao(nova_comissao NUMBER); END pkg_commissao;
comissao_padrao uma varivel global inicializada com 0.10 (10%). mudar_comissao um procedimento pblico usado para redefinir a comisso fixa com base em algumas regras de negcio. Ele implementado no corpo do pacote.
128
Corpo do Pacote
CREATE OR REPLACE PACKAGE BODY pkg_comissao IS function fn_validar(comissao number) RETURN boolean IS max_comissao tb_vendedor.comissao%TYPE; begin select max(comissao) into max_comissao from tb_vendedor; RETURN (comissao BETWEEN 0.0 AND max_comissao); end fn_validar; procedure prc_mudar_comissao(nova_comissao number) IS begin if fn_validar(nova_comissao) then comissao_padrao := nova_comissao; else raise_application_error(-20210, 'Comisso Invlida!'); end if; end prc_mudar_comissao; END pkg_comissao;
129
Utilizando o pacote:
Chamando um procedimento a partir do SQL*Plus: EXEC pkg_comissao.mudar_comissao(0.15); Chamando uma procedimento em um esquema diferente: EXEC scott.pkg_comissao.mudar_comissao(0.15); Para remover a especificao do pacote e do corpo: DROP PACKAGE pkg_comissao; Para remover somente o corpo do pacote: DROP PACKAGE BODY pkg_comissao; Para ver a especificao do pacote no dicionrio de dados: SELECT text FROM user_source WHERE name = 'PKG_COMISSAO' AND type = 'PACKAGE'; Para ver o corpo do pacote: SELECT text FROM user_source WHERE name = 'PKG_COMISSAO' AND type = 'PACKAGE BODY';
130
131
Design fcil:
Codificao da especificao e do corpo separadamente
Esconde informaes:
Somente as declaraes na especificao do pacote so visveis e acessveis para as aplicaes. Construes no corpo do pacote esto privadas e inacessveis.
Funcionalidades adicionais:
cursores
Melhor desempenho:
O pacote inteiro carregado na memria naprimeira vez que for referenciado. Existe apenas uma cpia na memria para todos os usurios. Sobrecarga: mltiplos subprogramas com o mesmo nome
132
Exerccios
1. Criar uma package chamado pkg_math com a seguinte estrutura:
a) Uma varivel global PI com o valor 3.14159265 b) Uma funo que receba como parmetro o raio, calcule e retorne a rea de um crculo ( * r2) c) Uma funo que receba como parmetro o raio e a altura de um cilindro, calcule e retorne seu volume ( * r2 * h) d) Um stored procedure para calcular o volume de uma esfera (4/3 * * r3). Deve-se passar 2 parmetros: o primeiro parmetro (tipo IN) com o valor do raio da esfera e o segundo parmetro (tipo OUT) receber o volume calculado.
133
Exerccio 1 - Soluo
create or replace package pkg_math is PI number := 3.14159265; function fn_area_circulo(raio number) return number; function fn_volume_cilindro(raio number, altura number) return number; procedure prc_volume_esfera(raio IN number, volume OUT number); end; create or replace package body pkg_math is function fn_area_circulo(raio number) return number is begin return PI * (raio*raio); end; function fn_volume_cilindro(raio number, altura number) return number is begin return PI * (raio*raio) * altura; end; procedure prc_volume_esfera(raio IN number, volume OUT number) is begin volume := 4/3 * PI * (raio*raio*raio); end; end;
134
Exerccio 1 Soluo
(cont.)
Declare r number := 10; a number := 20; x number; Begin x := pkg_math.fn_area_circulo(r); dbms_output.put_line('Um circulo de raio ' || r || ' tem rea ' || x); x := pkg_math.fn_volume_cilindro(r, a); dbms_output.put_line('Um cilindro de raio ' ||
pkg_math.prc_volume_esfera(r, x); dbms_output.put_line('Uma esfera de raio ' || r || ' tem volume ' || x); End;
135
Exerccios
2. Criar uma package chamado pkg_date com a seguinte estrutura:
a) Um campo privado chamado idioma com o valor inicial 'portuguese' b) Uma funo que receba como parmetro o novo idioma, que pode ser portuguese, english ou spanish, e altere a varivel global idioma. c) Uma funo que retorne o valor do idioma atual. d) Um stored procedure para obter a data por extenso. Devese passar 2 parmetros: o primeiro parmetro (tipo IN) com uma data no formato dd/mm/yyyy e o segundo parmetro (tipo OUT) receber a data por extenso no idioma atual.
136
Exerccio 2 - Soluo
create or replace package pkg_date is function fn_set_idioma(i varchar) return boolean; function fn_get_idioma return varchar; procedure prc_get_date(d IN date, extenso OUT varchar); end; create or replace package body pkg_date is idioma varchar(30) := 'portuguese'; function fn_set_idioma(p_idioma varchar) return boolean is begin if(p_idioma in('portuguese','spanish','english')) then idioma := p_idioma; return true; else return false; end if; end; function fn_get_idioma return varchar is begin return idioma; end; procedure prc_get_date(d IN date, extenso OUT varchar) is begin extenso := to_char(d,'DD/MONTH/YYYY', 'nls_date_language='||idioma); end; end;
137
Exerccio 2 Soluo
(cont.)
set serveroutput on; declare extenso varchar(80); begin if(pkg_date.fn_set_idioma('english')) then pkg_date.prc_get_date(sysdate,extenso); dbms_output.put_line(extenso || ' (' || pkg_date.fn_get_idioma || ')'); else dbms_output.put_line('Erro ao alterar idioma'); end if; end;
138
Trigger
Trigger (gatilho) um programa PL/SQL que disparado automaticamente a partir da execuo de seu evento associado. Os triggers so usados para automatizar tarefas. Quando existe uma sequencia de comandos a ser executado, o primeiro evento pode disparar um trigger que realiza as demais tarefas.
139
Trigger
(cont.)
Trigger
Sintaxe:
(cont.)
CREATE [OR REPLACE] TRIGGER <nome_trigger> BEFORE | AFTER <evento> ON <objeto> [FOR EACH ROW] BEGIN <bloco_de_instrues_pl_sql>; END;
2. EVENTO
INSERT UPDATE DELETE
3. NVEL
Instruo (STATEMENT) Linha (ROW)
4. CORPO
Bloco de instrues PL/SQL
142
Nvel de Trigger
Triggers podem ser disparadas:
No nvel de linha (Row level trigger) ou No nvel de instruo (Statement level trigger).
O cdigo de uma trigger em nvel de linha (FOR EACH ROW) executado uma vez para cada linha da tabela em que est sendo realizada uma instruo SQL.
Se voc executar uma instruo UPDATE que altera 10 linhas, o cdigo do trigger ser executado 10 vezes.
J na trigger em nvel de instruo, o seu cdigo executado apenas uma vez para qualquer instruo SQL
Este tipo de trigger ser executado apenas 1 vez, mesmo para uma instruo UPDATE que altera as 10 linhas.
143
Exemplo 1: Trigger para garantir que a descrio de produto seja gravada em letras maisculas.
CREATE OR REPLACE TRIGGER trg_produto_descricao BEFORE INSERT ON tb_produto FOR EACH ROW BEGIN :NEW.descricao := UPPER(:NEW.descricao); END; Para testar, preciso inserir um novo registro:
INSERT INTO tb_produto(cod_prod, descricao, preco) VALUES(100, 'caneta', 2.50); COMMIT;
Exemplo 3: Trigger para, caso o preo do produto seja negativo, converter para positivo.
CREATE OR REPLACE TRIGGER trg_produto_preco BEFORE INSERT ON tb_produto FOR EACH ROW BEGIN IF :NEW.preco < 0 THEN :NEW.preco := :NEW.preco * (-1); END IF; END;
Exemplo 4: Caso o campo estoque esteja vazio, gravar o valor ZERO utilizando uma trigger.
ALTER TABLE tb_produto ADD estoque integer; CREATE OR REPLACE TRIGGER trg_produto_estoque BEFORE INSERT ON tb_produto FOR EACH ROW BEGIN IF :NEW.estoque IS NULL THEN :NEW.estoque := 0; END IF; END; Para testar, vamos inserir um novo registro sem cdigo:
INSERT INTO tb_produto(descricao, preco) VALUES('mouse', 45.00); COMMIT;
148
Exemplo 6: Controle de histrico de preos. Ao inserir um novo produto ou atualizar seu preo, deve-se lanar em histrico.
Antes, vamos criar a tabela de histrico:
CREATE TABLE tb_produto_historico ( cod_prod_hist integer not null primary key, cod_prod integer not null, preco decimal(8,2), data date, FOREIGN KEY(cod_prod) REFERENCES tb_produto(cod_prod) ); CREATE SEQUENCE seq_produto_historico START WITH 1;
150
Exemplo 6: Controle de histrico de preos. Ao inserir um novo produto ou atualizar seu preo, deve-se lanar em histrico.
CREATE OR REPLACE TRIGGER trg_produto_historico AFTER INSERT OR UPDATE OF preco ON tb_produto FOR EACH ROW BEGIN INSERT INTO tb_produto_historico (cod_prod_hist,cod_prod, preco,data) VALUES(seq_produto_historico.NEXTVAL, :NEW.cod_prod, :NEW.preco, SYSDATE); END;
Ateno: A execuo de triggers no so retroativas. Ou seja, produtos inseridos anteriormente no sero lanados no histrico.
151
Exerccio 6 Para testar, vamos inserir e atualizar alguns produtos. Depois basta verificar se a tabela de histrico foi populada.
INSERT INTO tb_produto(descricao,preco,estoque) VALUES('Teclado', 50.00, 25); UPDATE tb_produto SET preco = 55.00 WHERE descricao = 'TECLADO'; UPDATE tb_produto SET preco = 60.00 WHERE descricao = 'TECLADO'; UPDATE tb_produto SET preco = 49.99 WHERE descricao = 'MOUSE'; COMMIT;
152
Exemplo 8: Impedir que a tabela de produtos seja manipulada (INSERT, UPDATE e DELETE) fora do horrio comercial.
CREATE OR REPLACE TRIGGER trg_horario_comercial BEFORE INSERT OR UPDATE OR DELETE ON tb_produto FOR EACH ROW BEGIN IF(TO_CHAR(SYSDATE,'HH24:MM') < '08:00'
OR TO_CHAR(SYSDATE,'HH24:MM') > '18:00' OR TO_CHAR(SYSDATE,'DY') IN ('SAB', 'DOM'))
THEN
RAISE_APPLICATION_ERROR(-20500,
'Operao no permitida nesse horrio');
Exerccios
1. Elaborar um trigger para gerar automaticamente o cdigo do funcionrio. 2. Elaborar um trigger para formatar nome e cargo do funcionrio em maisculas. 3. Elaborar um trigger para no aceitar que o salrio seja reduzido. 4. Elaborar um trigger para no permitir que o cdigo seja alterado. 5. Elaborar um trigger para no permitir que um funcionrio seja excludo. 6. Criar um trigger para registrar todo o histrico de cargos dos funcionrios, registrando o cargo e a data da mudana. 7. Alterar a tabela e a trigger que mantm o histrico para que seja registrado o usurio que realizou a alterao de cargo. 8. Criar uma tabela e um trigger para controlar o histrico de salrios dos funcionrios. 9. Criar um trigger para no permitir que o cargo de um funcionrio seja alterado 2 vezes no mesmo dia. 10. Criar um trigger para no permitir que o salrio de um funcionrio seja atualizado em menos de 3 meses.
156
158
CREATE OR REPLACE TRIGGER tg_funcionario_maiusculas BEFORE INSERT OR UPDATE ON tb_funcionario FOR EACH ROW BEGIN :NEW.nome := UPPER(:NEW.nome); :NEW.cargo := UPPER(:NEW.cargo); END;
159
CREATE OR REPLACE TRIGGER tg_funcionario_salario BEFORE UPDATE OF salario ON tb_funcionario FOR EACH ROW BEGIN IF :NEW.salario < :OLD.salario THEN RAISE_APPLICATION_ERROR(-20500, 'No permitido reduzir o salrio'); END IF; END;
160
CREATE OR REPLACE TRIGGER tg_funcionario_alterar_codigo BEFORE UPDATE OF cod_func ON tb_funcionario FOR EACH ROW BEGIN RAISE_APPLICATION_ERROR(-20501, 'No permitido alterar o cdigo'); END;
161
CREATE OR REPLACE TRIGGER tg_funcionario_alterar_codigo BEFORE DELETE ON tb_funcionario FOR EACH ROW BEGIN RAISE_APPLICATION_ERROR(-20501, 'No permitido excluir fisicamente um registro'); END;
162
Exerccio 6 Criar um trigger para registrar todo o histrico de cargos dos funcionrios, registrando o cargo e a data da mudana.
CREATE OR REPLACE TRIGGER tg_historico_cargo AFTER INSERT OR UPDATE OF cargo ON tb_funcionario FOR EACH ROW BEGIN INSERT INTO tb_historico_cargo(cod_func, cargo, data) VALUES(:NEW.cod_func, :NEW.cargo, SYSDATE); END;
163
Exerccio 7 Alterar a tabela e a trigger que mantm o histrico para que seja registrado o usurio que realizou a alterao de cargo.
ALTER TABLE tb_historico_cargo ADD usuario varchar(80); CREATE OR REPLACE TRIGGER tg_historico_cargo AFTER INSERT OR UPDATE OF cargo ON tb_funcionario FOR EACH ROW BEGIN INSERT INTO tb_historico_cargo (cod_func, cargo, data, usuario) VALUES(:NEW.cod_func, :NEW.cargo, SYSDATE, USER); END;
164
Exerccio 8 Criar uma tabela e um trigger para controlar o histrico de salrios dos funcionrios.
CREATE OR REPLACE TRIGGER tg_historico_salario AFTER INSERT OR UPDATE OF salario ON tb_funcionario FOR EACH ROW BEGIN INSERT INTO tb_historico_salario(cod_func, salario, data) VALUES(:NEW.cod_func, :NEW.salario, SYSDATE); END;
165
Exerccio 9 Criar um trigger para no permitir que o cargo de um funcionrio seja alterado 2 vezes no mesmo dia.
create or replace trigger tg_validar_cargo before update of cargo on tb_funcionario for each row declare v_qtde number; begin select count(*) into v_qtde from tb_historico_cargo where cod_func = :new.cod_func and trunc(data) = trunc(sysdate); if v_qtde <> 0 then raise_application_error(-20503,
'No permitido trocar de cargo no mesmo dia');
Exerccio 10 Criar um trigger para no permitir que o salrio de um funcionrio seja atualizado em menos de 3 meses.
create or replace trigger tg_validar_cargo before update of salario on tb_funcionario for each row declare v_qtde number; begin select count(*) into v_qtde from tb_historico_cargo where cod_func = :new.cod_func and trunc(data) >= trunc(add_months(sysdate,-3)); if v_qtde <> 0 then raise_application_error(-20503,
'Salrio j alterado a menos de 3 meses');
Exerccios
1. Criar uma tabela chamada tb_produto_estatistica com os campos: qtde_produtos e preco_medio. Cada vez que a tabela de produtos for manipulada (insert, update ou delete), deve-se disparar um trigger para atualizar os dados da tabela de estatsticas. 2. Criar uma tabela chamada tb_produto_auditoria com os campos: usuario, data, evento (Insert, Update, Delete), codigo_antigo, codigo_novo, descricao_antiga, descricao_nova, preco_antigo e preco_novo. Cada vez que a tabela de produtos for manipulada, deve-se disparar um trigger para criar a trilha para auditoria.
168
Tabelas de Pedidos
CREATE TABLE tb_pedido( cod_ped integer not null primary key, data date, valor decimal(8,2) default 0 ); CREATE TABLE tb_itens( cod_ped integer not null, cod_prod integer not null, qtde integer, PRIMARY KEY(cod_ped, cod_prod), FOREIGN KEY(cod_ped) REFERENCES tb_pedido(cod_ped), FOREIGN KEY(cod_prod) REFERENCES tb_pedido(cod_prod) ); UPDATE tb_produto SET estoque = 100;
170
Exerccio 1 Criar um trigger para dar baixa no estoque cada vez que um novo item for adicionado no pedido.
CREATE OR REPLACE tg_estoque_baixar AFTER INSERT ON tb_itens FOR EACH ROW BEGIN UPDATE tb_produto SET estoque = estoque - :NEW.qtde WHERE cod_prod = :NEW.cod_prod; END;
171
Exerccio 2 Criar um trigger para restaurar o estoque caso um item seja removido do pedido.
CREATE OR REPLACE tg_estoque_restaurar AFTER DELETE ON tb_itens FOR EACH ROW BEGIN UPDATE tb_produto SET estoque = estoque + :OLD.qtde WHERE cod_prod = :OLD.cod_prod; END;
172
Exerccio 3 Criar um trigger para ajustar o estoque caso a quantidade seja alterada.
CREATE OR REPLACE tg_estoque_ajustar AFTER UPDATE ON tb_itens FOR EACH ROW BEGIN UPDATE tb_produto SET estoque = estoque + :OLD.qtde - :NEW.qtde WHERE cod_prod = :NEW.cod_prod; END;
173
174
Exerccio 5 Criar uma nica trigger que ir fazer todo o controle do estoque.
CREATE OR REPLACE TRIGGER tg_estoque AFTER INSERT OR UPDATE OR DELETE ON tb_itens FOR EACH ROW BEGIN IF INSERTING THEN predicados condicionais UPDATE tb_produto SET estoque = estoque - :NEW.qtde WHERE cod_prod = :NEW.cod_prod; ELSIF DELETING THEN UPDATE tb_produto SET estoque = estoque + :OLD.qtde WHERE cod_prod = :OLD.cod_prod; ELSIF UPDATING THEN UPDATE tb_produto SET estoque = estoque + :OLD.qtde - :NEW.qtde WHERE cod_prod = :NEW.cod_prod; END IF; END;
175
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
176
177
178
179
Possveis Solues:
No use trigger
Uma maneira de evitar o erro de tabela mutante no usar trigger.
Resumo
PL/SQL Linguagem que rene recursos da linguagem SQL com instrues de controle tpicas de linguagens de programao procedurais. BLOCOS ANONIMOS instrues PL/SQL que permitem a realizao de rotinas junto ao banco de dados e que no ficam registrados no servidor. STORED PROCEDURES programas PL/SQL que permitem uso de passagem de parmetros e que, aps compilados, ficam gravados no servidor e podem ser executados atravs de linguagens de programao. FUNCTION programas PL/SQL que podem receber parmetros e que sempre retornam um resultado. TRIGGER programas PL/SQL que so disparados automaticamente pelo BD quando um evento associados executado.
183
Trabalho
(individual ou em dupla)
Elaborar um trigger para gerar automaticamente as parcelas das contas a receber a venda. A venda deve possuir: num_venda, data_venda, valor_venda, qtde_parcelas. Em contas a receber deve-se armazenar: num_venda, num_parcela, data_vecto, valor e situao ((P) pendente ou (Q) quitado). A partir do numero de parcelas da venda, deve-se criar as parcelas em contas a receber. A data da primeira parcela deve ser igual a data de venda. As demais parcelas devem ter o mesmo dia nos meses seguintes. A soma de todas as parcelas deve ser exatamente o valor total da venda. A situao inicial de todas as parcelas dever ser pendente.
184
Bibliografia
Livro Texto: 1. Introduo a Sistemas de Banco de Dados; Date, C. J.; Ed. Campus; 8 edio; 2004; 15 exemplares. 2. Sistemas de Banco de Dados; Korth, Henry F.; Ed. Campus; 5 edio; 2006; 15 exemplares. 3. Oracle Database 11g PL/SQL: Programao; McLaughlin, Michel; Ed. Novatec; 1 edio; 2009; 10 exemplares. Complementar: 4. Projeto e Modelagem de Banco de Dados; Lightstone, Teorey; Ed. Campus; 1 edio; 2006; 10 exemplares. 5. Oracle 10g PL/SQL; Oliveira, Celso H. Poderoso de; Ed. Novatec; 1 edio; 2005; 10 exemplares. 6. Oracle 10g Database: Guia do DBA; Serson, Roberto Rubinstein; Ed. Novatec; 1 edio; 2004; 10 exemplares. 7. Oracle 10g: Ideal Para Quem Deseja Iniciar o Aprendizado do Oracle; Ramalho, Jos A.; Ed. Thomson; 1 edio; 2005; 10 exemplares. 8. Gerenciando Banco de Dados: A Abordagem Entidade-Relacionamento para Projeto Lgico; Chen, Peter; Makron Books; 1 edio; 1990; 7 exemplares.
185