You are on page 1of 185

Administrao de Banco de Dados Linguagem PL/SQL

Prof. Dr. Wagner J. Dizer


wagner@unilins.edu.br

Lins-SP, Agosto de 2012

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

Definio Blocos annimos Cursores Stored procedures Functions Triggers

Oracle 11g

Instalao e Servios iSQLPlus Criao de um usurio DBA

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

Criao de um usurio DBA


Para utilizar o Oracle, necessrio possuir um usurio (ou uma conta) atravs da qual possvel se conectar ao banco de dados. Sintaxe: CREATE USER <usuario> IDENTIFIED BY <senha>; Para se poder criar um usurio, preciso estar conectado com privilgio de administrador (SYSTEM). CONN SYSTEM / ORACLE @ ORCL;
usurio senha banco
9

Criao de um usurio DBA

(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

Linguagem Procedural PL/SQL


A linguagem PL/SQL uma extenso do SQL Combina o poder de manipulao de dados do SQL com o poder de processamento das linguagens de programao Estruturada em blocos Permite a criao de variveis Permite controle do fluxo de execuo (IF, FOR) Permite integrao entre diferentes ferramentas Oracle No permite comandos DDL No tem nenhuma funcionalidade de entrada ou de sada construda diretamente na linguagem O PL/SQL no padronizado nos diversos SGBDs.
11

Vantagens do uso de PL/SQL


Possui maior poder de processamento
(consultas e controle de fluxo)

Pode melhorar o desempenho de uma aplicao


(reaproveita o mesmo plano de execuo)

Diminui o trafego de dados pela rede


(vrias consultas pode ser encapsuladas numa nica requisio)

Controle centralizado no servidor


(processamento centralizado)

Pode ser utilizado por diversas ferramentas


(forms/reports, java, php, ...)

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

Exemplo 01: Criando uma varivel e apresentado seu valor.

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

Exemplo 02: Criando uma varivel de substituio.

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

Exemplo 03: Tratando excees


SET SERVEROUTPUT on; DECLARE S varchar2(10); BEGIN S := '&input'; dbms_output.put_line('Valor de S ' || S); EXCEPTION WHEN others THEN dbms_output.put_line(SQLERRM); END;
Um possvel erro a digitao de uma string maior que o tamanho definido da varivel S.
16

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

Estrutura de Controle Condicional


Estruturas de controle fazem a verificao de uma condio lgica e a ramificao da execuo de programas de acordo com as expresses booleanas. Em PL/SQL, possvel criar desvios condicionais atravs de IF e CASE. Todos statements IF so blocos e terminam com END IF.
H 2 subtipos: if-then-else e if-then-elsif-then-else.

Os statements CASE tambm so blocos e terminam com END CASE. A seguir, veremos exemplos do uso de IF e CASE.

18

IF, IF ... ELSE


BEGIN IF 1=1 THEN dbms_output.put_line('Equal'); END IF; END;

BEGIN IF 1=2 THEN dbms_output.put_line('Equal'); ELSE dbms_output.put_line('Different'); END IF; END;


19

IF ... ELSIF ... ELSE

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;

BEGIN IF 1=1 OR 1=2 THEN dbms_output.put_line('True'); END IF; END;


21

Operadores Lgicos

(cont.)

BEGIN IF 1 BETWEEN 1 AND 3 THEN dbms_output.put_line('In the range'); END IF; END;

BEGIN IF 1 IN (1,2,3) THEN dbms_output.put_line('In the set'); END IF; END;

22

Operadores Lgicos

(cont.)

BEGIN IF 'Str%' LIKE 'String' THEN dbms_output.put_line('Match'); END IF; END;

BEGIN IF NOT FALSE THEN dbms_output.put_line('True'); END IF; END;

23

Operadores Lgicos

(cont.)

DECLARE var BOOLEAN; BEGIN IF var IS NULL THEN dbms_output.put_line('It is null'); END IF; END;

24

Exemplo 04: Calcular a mdia e verificar se foi aprovado.


DECLARE nota1 number := 8; nota2 number := 7; media number; BEGIN media:= (nota1+nota2) / 2; IF media >= 6.0 THEN dbms_output.put_line('Voc foi aprovado!'); ELSE dbms_output.put_line('Voc foi reprovado'); END IF; END;
25

Exemplo 05: Comparar valores.


BEGIN CASE true WHEN (1>3) THEN
dbms_output.put_line('Um maior que trs');

WHEN (3<5) THEN


dbms_output.put_line('Trs menor que cinco');

WHEN (1=2) THEN


dbms_output.put_line('Um igual a dois');

ELSE
dbms_output.put_line('Nenhuma das alternativas');

END CASE; END;


26

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

Exemplo 06: Apresentar a tabuada do nmero 3 (FOR).

DECLARE N number := 3; R number; BEGIN FOR I IN 1..10 LOOP R := N * I;


dbms_output.put_line(N || 'x' || I || ' = ' || R);

END LOOP; END;

28

Exemplo 07: Tabuada (LOOP simples).


DECLARE N number := 3; I number := 1; R number; BEGIN LOOP EXIT WHEN I > 10; R := N * I;
dbms_output.put_line(N || 'x' || I || ' = ' || R);

I := I + 1; END LOOP; END;


29

Exemplo 08: Tabuada (WHILE).

DECLARE N number := 3; I number := 1; R number; BEGIN WHILE I <= 10 LOOP R := N * I;


dbms_output.put_line(N || 'x' || I || ' = ' || R);

I := I + 1; END LOOP; END;

30

Uso de SQL em blocos annimos

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

Exemplo 09: Obter a quantidade de registros de uma tabela.


CREATE TABLE tb_teste ( codigo integer not null primary key, descricao varchar(40) ); CREATE SEQUENCE seq_teste START WITH 1; INSERT INTO tb_teste (codigo, descricao) VALUES (seq_teste.NEXTVAL, 'Teste 1'); INSERT INTO tb_teste (codigo, descricao) VALUES (seq_teste.NEXTVAL, 'Teste 2'); INSERT INTO tb_teste (codigo, descricao) VALUES (seq_teste.NEXTVAL, 'Teste 3'); COMMIT;

DECLARE qtde number; BEGIN SELECT count(*) INTO qtde FROM tb_teste; dbms_output.put_line(' Existem ' || qtde || ' registros.'); END;
32

Exemplo 10: Apresentar os registros de uma tabela.

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

Exemplo 11: Inserir novos registros via PL/SQL.

BEGIN FOR I IN 4..10 LOOP INSERT INTO tb_teste (codigo, descricao) VALUES (seq_teste.NEXTVAL, 'Teste ' || I); END LOOP; END;

ps. Executar o exemplo anterior para ver se os registros foram gravados.

34

Exemplo 12: Controle de transao.


BEGIN SAVEPOINT init_transaction; FOR I IN 11..20 LOOP INSERT INTO tb_teste (codigo, descricao) VALUES (seq_teste.NEXTVAL, 'Teste ' || I); END LOOP; COMMIT; EXCEPTION WHEN others THEN ROLLBACK TO init_transaction; dbms_output.put_line(SQLERRM); END;
ps. Agora temos a garantia de que toda a transao seja realizada ou, caso ocorra algum erro durante a transao, toda a operao ser desfeita.
35

Exemplo 13: Excluir os registros com cdigos pares.

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');

END IF; END;

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

1. Criar um bloco annimo que compare 2 nmeros e apresente o maior valor.


set serveroutput on; declare n1 number := 3; n2 number := 5; begin if (n1>n2) then dbms_output.put_line(n1 || ' maior'); else dbms_output.put_line(n2 || ' maior'); end if; end;

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

6. Fazer um bloco annimo para verificar se o ano atual par.


set serveroutput on; declare ano number; begin select extract(year from sysdate) into ano from dual; if ano mod 2 = 0 then dbms_output.put_line(ano || ' par'); else dbms_output.put_line(ano || ' mpar'); end if; end;

46

7. Fazer um bloco annimo para verificar se o ano atual bissexto.


set serveroutput on; declare ano number; begin select extract(year from sysdate) into ano from dual; if ano mod 4 = 0 then dbms_output.put_line(ano || ' bissexto'); else dbms_output.put_line(ano || ' no bissexto'); end if; end;

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.

Por questo de desempenho, o uso de cursores fortemente recomendado. Sintaxe:


declare cursor <nome_cursor> is <consulta_sql>;
51

Criar as tabelas produto e categoria


create table tb_categoria ( cod_cat integer not null primary key, descricao varchar(60) ); create table tb_produto ( cod_prod integer not null primary key, descricao varchar(60), preco decimal(8,2), cod_cat integer, foreign key(cod_cat) references tb_categoria(cod_cat) );

52

Cadastrar alguns registros


create sequence sq_produto start with 1 increment by 1; insert into tb_categoria (cod_cat, descricao) values (1,'Informtica'); insert into tbl_categoria (cod_cat, descricao) values (2,'Papelaria'); insert into tb_produto (cod_prod,decricao,preco,cod_cat) values (sq_produto.nextval, 'Impressora', 250.00, 1); insert into tb_produto (cod_prod, decricao,preco,cod_cat) values (sq_produto.nextval, 'mouse', 49.90, 1); insert into tb_produto (cod_prod, decricao,preco,cod_cat) values (sq_produto.nextval, 'papel', 15.00, 2); insert into tb_produto (cod_prod, decricao,preco,cod_cat) values (sq_produto.nextval, 'grampeador', 7.50, 2); commit;
53

Cursor na forma expandida


Declare cursor C is select descricao, preco from tb_produto; R C%rowType; Begin Open C; Loop Fetch C into R; Exit when C%NotFound; dbms_output.put_line(R.descricao || ' ' || R.preco); End loop; Close C; End;

No exemplo, o cursor foi declarado de forma explcita.


54

Cursor na forma simplificada

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

Apresentar a descrio do produto, preo do produto e a descrio da categoria.

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.

Note que o cursor pode obter dados de uma ou mais tabelas.

56

Cursor recebendo parmetro

Apresentar uma listagem de cada categoria com seus respectivos produtos utilizando quebra de sesso por categoria.
CATEGORIA HIGIENE:

- Sabonete R$ 0,80 - Shampoo R$ 4,20


CATEGORIA BEBIDA:

Coca-Cola R$ 4,50 Suco de uva R$ 7,80

57

Cursor recebendo parmetro


Declare cursor CC is select cod_cat, descricao from tb_categoria; cursor CP(p_cod_cat number) is select descricao, preco from tb_produto where cod_cat = p_cod_cat;

(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

Exemplo 1: Stored Procedure para cadastrar novos produtos.

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;

Para executar, use: EXEC prc_inserir_produto('iPad',1500.00); Em caso de erros: SHOW ERRORS;


60

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

Passagem de Parmetro IN OUT


Existem 3 formas de passagem de parmetro em PL/SQL: IN, OUT e IN OUT.
Com IN (forma default), o parmetro de entrada. Com OUT, a passagem por referncia.

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

Erros de execuo (run-time)


Erros de run-time podem ocorrer na declarao, execuo e blocos de exceo PL/SQL. Erros levantados no bloco de execuo so lanados no bloco de exceo local.
Funo SQLCODE Erros pr-definidos pelo Oracle
Retorna um nmero negativo que mapeia as excees prdefinidas do Oracle. Retorna o cdigo de erro e mensagem para uma exceo.

Erros definidos pelo usurio


Retorna um 1 positivo se no existe EXCEPTION_INIT PRAGMA definida. Se estiver definida, retorna um nmero negativo entre -20001 e -20999. Retorna um 1 e uma mensagem definida pelo usurio se disparada pelo comando RAISE. Caso contrrio, retorna um cdigo de erro entre -20001 e -20999 e uma mensagem de texto ajustada por RAISE_APPLICATION_INFO.
84

SQLERRM

Funes Pr-definidas de Excees


EXCEO
ACCESS_INTO_NULL CASE_NOT_FOUND CURSOR_ALREADY_OPEN DUP_VAL_ON_INDEX INVALID_CURSOR INVALID_NUMBER LOGIN_DENIED NO_DATA_FOUND NOT_LOGGED_ON

(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

Funes Pr-definidas de Excees


EXCEO
PROGRAM_ERROR ROWTYPE_MISMATCH STORAGE_ERROR SYS_INVALID_ROWID TIMEOUT_ON_RESOURCE TOO_MANY_ROWS VALUE_ERROR ZERO_DIVIDE

(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

Diretrizes para a Captura de Excees


A palavra-chave EXCEPTION inicia a seo de tratamento de excees. WHEN OTHERS a ltima clusula. So permitidos vrios handlers de exceo. Somente um handler processado antes de se sair do bloco. permitido o uso de operadores lgicos (AND, OR, NOT)

87

Exemplo 1 Consultar preo de um produto.


DECLARE v_descricao tb_produto.descricao%type := '&p_descricao'; v_preco tb_produto.preco%type; BEGIN SELECT preco INTO v_preco FROM tb_produto WHERE descricao = v_descricao; dbms_output.put_line('Preo: ' || v_preco); EXCEPTION WHEN NO_DATA_FOUND THEN dbms_output.put_line('NENHUM REGISTRO ENCONTRADO.'); WHEN TOO_MANY_ROWS THEN dbms_output.put_line('PRODUTOS COM MESMA DESCRIO.'); WHEN OTHERS THEN dbms_output.put_line('ERRO DESCONHECIDO.' ); END;
88

Usando Stored Procedure com parmetro OUT


CREATE OR REPLACE PROCEDURE prc_buscaProduto( p_codigo IN number, p_descricao OUT varchar2, p_preco OUT number) is BEGIN SELECT descricao, preco INTO p_escricao, p_preco FROM tb_produto WHERE cod_prod = p_codigo; EXCEPTION WHEN NO_DATA_FOUND THEN p_descricao := 'Produto no localizado'; p_preco := 0; END;
89

Usando Stored Procedure com parmetro OUT

(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

Exemplo 2 Chave primria duplicada.


DECLARE v_descricao tb_produto.descricao%TYPE; v_preco tb_produto.preco%TYPE; v_cod_cat tb_produto.cod_cat%TYPE; BEGIN v_descricao := 'Pen drive'; v_preco := 44.99; v_cod_cat := 1; INSERT INTO tb_produto VALUES(1, v_descricao, v_preco, v_cod_cat); dbms_output.put_line ('Insero executada com sucesso.'); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN dbms_output.put_line ('Cdigo j cadastrado.' ); WHEN Others THEN dbms_output.put_line ('Erro no cadastramento.' ); END;
91

Exemplo 3 Valor muito grande para o campo.

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

Exemplo 4 Valor numrico invlido.

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

Exemplo 5 Diviso por ZERO.

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

Possvel gravar os erros ocorridos


DECLARE v_code number; v_erro varchar(100); BEGIN... EXCEPTION... WHEN OTHERS THEN ROLLBACK; v_code := SQLCODE; v_erro := SQLERRM; INSERT INTO tb_erros VALUES(v_code, v_erro); END;
Obs. preciso criar a tabela de erros. create table tb_erros ( codigo_erro number, mensagem_erro varchar2(100) );
95

Declarando excees do usurio


Uma exceo declarada como qualquer outra varivel PL/SQL.
DECLARE e EXCEPTION; BEGIN RAISE e; dbms_output.put_line('Essa linha no ser executada'); EXCEPTION WHEN OTHERS THEN IF SQLCODE = 1 THEN dbms_output.put_line('SQLERRM'); END IF; END;

Resposta: [User-Defined Exception]


96

Exemplo 1 Alterar um produto


DECLARE e_invalid_product EXCEPTION; BEGIN UPDATE tb_produto SET descricao = '&product_description' WHERE cod_prod = &product_number; IF SQL%NOTFOUND THEN RAISE e_invalid_product; END IF; COMMIT; EXCEPTION WHEN e_invalid_product THEN DBMS_OUTPUT.PUT_LINE('Cdigo de produto invlido.'); END;
97

Exemplo 2 Cadastrar Categoria


DECLARE v_codigo tb_categoria.cod_cat%Type; v_descricao tb_categoria%Type; campos_obrigatorios EXCEPTION; BEGIN -- v_codigo := 1; v_descricao := 'Material Esportivo'; IF (codigo IS NULL) OR (descricao IS NULL) THEN RAISE campos_obrigatorios; ELSE INSERT INTO tb_categoria(cod_cat, descricao) VALUES (v_codigo, v_descricao); END IF; EXCEPTION WHEN campos_obrigatorios THEN dbms_output.put_line ('Preencher todos os campos.'); WHEN Dup_Val_On_Index THEN dbms_output.put_line ('Cdigo j cadastrado.' ); WHEN Others THEN dbms_output.put_line ('Erro no cadastramento.' ); END;

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

Trabalho (individual ou em dupla) Sistema para cotao de preos


FORNECEDOR
cod_forn razao_social nome_fantasia cnpj ie endereco cidade telefone
0..N 0..N

(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

Trabalho: Sistema para cotao de preos

(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

Trabalho: Sistema para cotao de preos

(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

Como executar uma funo:


Com chamada:
CALL function_name(parameter) INTO target_variable_name;

Dentro de blocos PL/SQL:


target_variable_name := function_name(parameter);

Com SQL:
SELECT function_name(parameter) FROM table_name;

Obs.: caso a funo no tenha parmetro, basta omitir.


106

Exemplo 1: Funo que recebe um valor e retorna o seu quadrado.


CREATE FUNCTION fn_quadrado(n IN number) RETURN number IS q number; BEGIN q := n * n; RETURN q; END; Para executar: SELECT fn_quadrado(5) FROM dual;

107

Exemplo 2: Funo que retorne o dia de amanh.


CREATE OR REPLACE FUNCTION fn_amanha RETURN date IS d date; BEGIN SELECT SYSDATE+1 INTO d FROM DUAL; RETURN d; END; Para executar: SELECT fn_amanha FROM dual;

108

Exemplo 3: Retornar a mdia de preos dos produtos.


CREATE OR REPLACE FUNCTION fn_media_precos RETURN number IS media number; BEGIN SELECT AVG(preco) INTO media FROM tb_produto; RETURN media; END; Para executar: SELECT fn_media_precos FROM dual;
109

Exemplo 4: Recebe preo do produto e percentual de desconto, retorna o valor da promoo.


CREATE OR REPLACE FUNCTION fn_promocao (preco number, percentual number) RETURN number IS BEGIN RETURN preco - (preco * percentual / 100); END;
Teste 1: SELECT fn_promocao(100, 5) FROM dual; Teste 2: SELECT descricao, preco, fn_promocao(preco, 5) promocao FROM tb_produto;
obs.: a funo executada para cada registro encontrado na tabela tb_produto.

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

1. Criar uma funo que receba um nmero e retorne seu fatorial.


create or replace FUNCTION fatorial(n IN number) RETURN number IS f number := 1; BEGIN FOR i IN 1..n LOOP f := f * i; END LOOP; RETURN f; END; SELECT fatorial(5) FROM dual;
113

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

3. Crie uma funo que receba o nmero do ms e retorne o nome deste.


CREATE OR REPLACE FUNCTION fn_mes_extenso(p_mes number) RETURN varchar IS v_mes varchar(30); BEGIN CASE
WHEN WHEN WHEN WHEN WHEN WHEN WHEN WHEN WHEN WHEN WHEN WHEN ELSE p_mes= 1 p_mes= 2 p_mes= 3 p_mes= 4 p_mes= 5 p_mes= 6 p_mes= 7 p_mes= 8 p_mes= 9 p_mes=10 p_mes=11 p_mes=12 v_mes := THEN v_mes := THEN v_mes := THEN v_mes := THEN v_mes := THEN v_mes := THEN v_mes := THEN v_mes := THEN v_mes := THEN v_mes := THEN v_mes := THEN v_mes := THEN v_mes := 'Invlido!'; 'Janeiro'; 'Fevereiro'; 'Maro'; 'Abril'; 'Maio'; 'Junho'; 'Julho'; 'Agosto'; 'Setembro'; 'Outubro'; 'Novembro'; 'Dezembro';

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

7. Criar uma funo que receba um CPF e retorne se vlido. (1/2)


CREATE OR REPLACE FUNCTION fn_valida_cpf (p_cpf IN CHAR) RETURN BOOLEAN IS m_total NUMBER := 0; m_digito NUMBER := 0; BEGIN FOR i IN 1 .. 9 LOOP m_total := m_total + SUBSTR (p_cpf, i, 1) * (11 - i); END LOOP; m_digito := 11 - MOD (m_total, 11); IF m_digito > 9 THEN m_digito := 0; END IF; IF m_digito != SUBSTR (p_cpf, 10, 1) THEN RETURN FALSE; END IF;
120

7. Criar uma funo que receba um CPF e retorne se vlido. (2/2)


m_digito := 0; m_total := 0; FOR i IN 1 .. 10 LOOP m_total := m_total + SUBSTR (p_cpf, i, 1) * (12 - i); END LOOP; m_digito := 11 - MOD (m_total, 11); IF m_digito > 9 THEN m_digito := 0; END IF; IF m_digito != SUBSTR (p_cpf, 11, 1) THEN RETURN FALSE; END IF; RETURN TRUE; END;
121

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;

Ex.: SELECT cod_prod, descricao, preco, FN_CLASSIFICAR(preco) FROM tb_produto;

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

Partes do Package: visibilidade

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

Orientaes para Escrita de Pacotes


Construa pacotes para uso geral. Defina a especificao do pacote antes do corpo. A especificao do pacote deve conter apenas as construes que voc quer que seja pblico. Mudanas nas especificaes do pacote requer recompilao de cada subprograma referncia. A especificao do pacote deve conter o mnimo de construes possvel.

131

Vantagens no Uso de Pacotes


Facilita a manuteno:
Mantm a funcionalidade e a lgica juntas

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 ' ||

r || ' e altura ' || a || ' tem volume ' || x);

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.)

Como principais motivos para o uso de database trigger, podemos citar:


gerao automtica de valores derivados de colunas preveno de transaes invlidas reforar regras de negcio complexas prover auditoria gerar estatsticas sobre acesso s tabelas prover log de transaes
140

Trigger
Sintaxe:

(cont.)

CREATE [OR REPLACE] TRIGGER <nome_trigger> BEFORE | AFTER <evento> ON <objeto> [FOR EACH ROW] BEGIN <bloco_de_instrues_pl_sql>; END;

BEFORE | AFTER - indicao o momento em que o trigger ser disparado.


BEFORE indica que o corpo do trigger ser executado antes do comando que a disparou. AFTER indica que o corpo do trigger ser executado aps o comando que a disparou.
141

Um trigger composto por 4 partes:


1. MOMENTO
BEFORE (tabela) AFTER (tabela) INSTEAD OF (view)

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

Registros Especiais :NEW e :OLD


Os registros especiais :NEW e :OLD armazenam temporariamente os valores do ltimo registro manipulado pelo SGBD.
Ao inserir, criado o registro :NEW Ao apagar, criado o registro :OLD Ao atualizar, so criados os 2 registros :NEW e :OLD
DML INSERT DELETE UPDATE :OLD NULO Valores antigos Valores antigos :NEW Valores novos NULO Valores Novos
144

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;

Para exibir os erros de compilao: SHOW ERRORS;


145

Exemplo 2: Trigger para gerao automtica do cdigo.


CREATE SEQUENCE seq_produto START WITH 101; CREATE OR REPLACE TRIGGER trg_produto_codigo BEFORE INSERT ON tb_produto FOR EACH ROW BEGIN SELECT seq_produto.NEXTVAL INTO :NEW.cod_prod FROM DUAL; END; Para testar, vamos inserir um novo registro sem cdigo:
INSERT INTO tb_produto(descricao, preco) VALUES('lpis', 1.25); COMMIT;
146

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;

Para testar, vamos inserir um novo registro sem cdigo:


INSERT INTO tb_produto(descricao, preco) VALUES('borracha', -2.99); COMMIT;
147

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 5: Trigger para remover espaos em branco na descrio ao inserir ou atualizar.


CREATE OR REPLACE TRIGGER trg_produto_descricao
BEFORE INSERT OR UPDATE OF descricao ON tb_produto

FOR EACH ROW BEGIN :NEW.descricao := TRIM(:NEW.descricao); END;


UPDATE tb_produto SET descricao = ' MOUSE ', preco = 44.99 WHERE descricao = 'MOUSE'; COMMIT; Ao indicar UPDATE OF descricao, o gatilho s ser disparado se a descrio for atualizada. Desta forma, se apenas o preo for modificado, essa trigger no ser executada.
149

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 7: Iremos registrar, tambm, o usurio responsvel pela alterao de preo.


ALTER TABLE tb_produto_historico ADD usuario varchar(100); 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,usuario) VALUES(seq_produto_historico.NEXTVAL, :NEW.cod_prod, :NEW.preco, SYSDATE, USER); END;
153

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');

END IF; END;


154

Habilitando e Desabilitando um trigger


Algumas vezes, quando se precisa dar manuteno na base de dados, precisando desligar temporariamente alguns triggers, para isso, usamos:
ALTER TRIGGER <nome_trigger> DISABLE; ou ALTER TABLE <nome_tabela> DISABLE ALL TRIGGERS;

Para novamente habilitar o trigger, usamos:


ALTER TRIGGER <nome_trigger> ENABLE;

Caso se queira excluir definitivamente um trigger:


DROP TRIGGER <nome_trigger>;
155

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

Tabelas de Funcionrios e Histricos


CREATE TABLE tb_funcionario( cod_func integer not null primary key, nome varchar(80), cargo varchar(40), salario decimal(8,2), situacao char(1) default 'A', check(situacao IN('A','I')) -- Ativo ou Inativo ); CREATE TABLE tb_historico_cargo( cod_func integer not null, cargo varchar(40), data date ); CREATE TABLE tb_historico_salario( cod_func integer not null, salario decimal(8,2), Para simplificar: data date - cargo foi criado como um atributo. - omitiu-se as PKs e FKs no histrico. );
157

Exerccio 1 Elaborar um trigger para gerar automaticamente o cdigo do funcionrio.


CREATE SEQUENCE sq_funcionario START WITH 1 INCREMENT BY 1; CREATE OR REPLACE TRIGGER tg_funcionario_codigo BEFORE INSERT ON tb_funcionario FOR EACH ROW BEGIN SELECT sq_funcionario.NEXTVAL INTO :NEW.cod_func FROM dual; END;

158

Exerccio 2 Elaborar um trigger para formatar nome e cargo do funcionrio em maisculas.

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

Exerccio 3 Elaborar um trigger para no aceitar que o salrio seja reduzido.

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

Exerccio 4 Elaborar um trigger para no permitir que o cdigo seja alterado.

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

Exerccio 5 Elaborar um trigger para no permitir que um funcionrio seja excludo.

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');

end if; end;


166

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');

end if; end;


167

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

Controle de Estoque por Trigger


1. Criar um trigger para dar baixa no estoque cada vez que um novo item for adicionado no pedido. 2. Criar um trigger para restaurar o estoque caso um item seja removido do pedido. 3. Criar um trigger para ajustar o estoque caso a quantidade seja alterada. 4. Remover os 3 triggers criados para controlar o estoque de produtos. 5. Criar um nico trigger que ir fazer todo o controle do estoque (insert, update e delete). 6. Criar um nico trigger que ir fazer a atualizao do campo valor na tabela de pedido, sempre que a tabela de itens for manipulada.
169

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

Exerccio 4 Remover os 3 triggers criados para controlar o estoque de produtos.

DROP TRIGGER tg_estoque_baixar; DROP TRIGGER tg_estoque_restaurar; DROP TRIGGER tg_estoque_ajustar;

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

Trigger para Eventos do Sistema


Num trigger, podemos fazer referencia a um evento do sistema ao invs de referenciarmos uma tabela. Os eventos de sistema so:
AFTER SERVERERROR AFTER LOGON BEFORE LOGOFF AFTER STARTUP BEFORE SHUTDOWN

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

Log para Controle de Acessos


Vamos criar a tabela de log de acesso e seu sinnimo:
CREATE TABLE tb_log_acesso ( usuario varchar(80), horario char(20), evento char(6) ); CREATE PUBLIC SYNONYM log_acesso_ID FOR SYSTEM.tb_log_acesso;

177

Trigger para registrar o logon


CREATE OR REPLACE TRIGGER tg_registrar_logon AFTER LOGON ON DATABASE BEGIN INSERT INTO log_acesso_ID VALUES( USER, TO_CHAR(sysdate, 'DD-MM-YYYY HH24:MI:SS'), 'LOGON'); COMMIT; END;

178

Trigger para registrar o logoff


CREATE OR REPLACE TRIGGER tg_registrar_logoff BEFORE LOGOFF ON DATABASE BEGIN INSERT INTO log_acesso_ID VALUES( USER, TO_CHAR(sysdate, 'DD-MM-YYYY HH24:MI:SS'), 'LOGOFF'); COMMIT; END;

179

Trigger com Tabela Mutante


Uma tabela mutante (MUTATING TABLE) uma tabela que est sendo modificada atualmente por uma instruo UPDATE, DELETE ou INSERT. Quando criamos um trigger para determinada tabela e, dentro dela, tentamos manipular os dados dessa mesma tabela, enfrentamos o problema de trigger mutante. O cdigo de erro Oracle ORA-04091 Isso ocorre porque o trigger executado no momento de manipulao da tabela e o objeto (tabela) est em modo bloqueado at que se encerre. Uma tabela s considerada mutante para gatilhos de linha (FOR EACH ROW).
180

Exemplo de Trigger Mutante


create or replace trigger tg_verificar_reserva_quarto before insert or update on tb_reservas for each row declare cursor cur_reservas is select * from tb_reservas where num_quarto = :new.num_quarto and (dt_entrada between :new.dt_entrada and :new.dt_saida or dt_saida between :new.dt_entrada and :new.dt_saida); begin for reg_reservas in cur_reservas loop raise_application_error(-20001, 'Quarto j possui reserva para este perodo'); end loop; end;
181

Possveis Solues:
No use trigger
Uma maneira de evitar o erro de tabela mutante no usar trigger.

Use after or "instead of


Se voc usar um gatilho AFTER para um trigger que ir atualizar uma tabela, a atualizao j estar concluda quando o trigger for executado e a tabela no ser mutante.

Reescreva a sintaxe da trigger


Existem algumas maneiras de evitar o erro de tabela mutante com uma combinao de gatilhos de nvel de linha e de instruo.

Use autonomous transactions


Voc pode evitar o erro de tabela mutante, marcando seu gatilho como uma transao autnoma, tornando-se independente da tabela que chama o procedimento.
182

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

You might also like