Vous êtes sur la page 1sur 71

Programao em PL/SQL

Estrutura do Curso
1. Introduo a PL/SQL
2. Conceitos Bsicos de PL/SQL
3. Variveis e Dados de Programa
4. Controle Condicional e Seqencial
5. Loops
6. Registros em PL/SQL
7. Tabelas PL/SQL
8. SQL em PL/SQL
9. Funes de SQL integradas
10. Cursores
11. Procedimentos e Funes
12. Packages
13. Triggers
14. Tratamento de Erros
15. Testes e Depurao
16. PL/SQL Dinmico
17. Entrada e Sada em Arquivos
18. Desempenho e Otimizao
PL/SQL

Procedural Language extensions to SQL. Usamos esta linguagem no Oracle Server
e em aplicaes-cliente (p.e. Oracle Forms). Adiciona construes de programao no
existentes na linguagem de banco de dados padro. Permite a combinao de comandos
SQL com construes procedurais.




Ambientes de Execuo
Servidor
Oracle 7: PL/SQL verso 2
Oracle 8: PL/SQL verso 8
SQL* Plus (modo interativo)
Cliente
Oracle Developer (Forms , Reports e
Procedure Builder)
Utilizam compiladores PL/SQL prprios
Conceitos Bsicos da Linguagem PL/SQL

Character Set do PL/SQL



O PL/SQL no uma linguagem sensvel ao contexto.
Letras maisculas so tratadas da mesma maneira que minsculas, a no ser no caso
destas pertencerem a uma cadeia de caracteres.
If x < > s then
If x < > S then

Smbolos Simples e Compostos



Caracteres so agrupados, formando unidades lxicas, que so os menores
componentes individuais da linguagem. Uma unidade lxica pode formar:
identificadores
literais
delimitadores
comentrios

Identificadores
Identificador um nome para um objeto PL/SQL.
constante, varivel, exception, procedimento, funo, package, registro, tabela PL/SQL,
cursor e palavra reservada.
At 30 caracteres.
Tem que comear com uma letra.
Pode incluir $, _ e # .
No pode conter espaos.

Literais
Literal um valor no representado por um identificador; simplesmente um valor.
Nmero
415, 21.6 ou NULL
String
Esta uma frase , 01-03-97 ou NULL
Boolean
TRUE, FALSE ou NULL

Delimitadores
Delimitador Ponto e vrgula (;)
Indica o final de um comando
IF salario < min_salario
THEN
salario := salario + salario * .25;
END IF;

Comentrios
Comentrio de uma nica linha
IF salario < min_salario(1994) -- retorna min salrio do ano
THEN
salario := salario * .25;
END IF;
Comentrio de mltiplas linhas
PROCEDURE calcula_receita (empresa IN NUMBER) IS
/ * Programa alterado em 23/9/94
Analista Responsvel: Manoel de Souza * /
...

Estrutura de um Bloco

A estrutura da linguagem PL/SQL orientada a blocos
Modularizao
um bloco a unidade bsica de trabalho da
qual procedimentos e funes so construdos
Escopo
o bloco oferece um escopo ou contexto para
objetos logicamente relacionados

Sees de um Bloco
Cabealho (Header)
determina o modo como o bloco deve ser chamado
Seo de Declarao
onde declaramos variveis, cursores e sub-blocos e
sees de exceo
Seo de Execuo
parte que contm o cdigo a ser executado
Seo de Excees (Exceptions)
manipula excees ao processamento normal
(condies de aviso e erro).
Sees de um Bloco








Variveis e Dados de Programa

Variveis
Atributos de uma varivel
nome, tipo e valor
Nome
Pode ter at 30 caracteres
Tem que comear com uma letra
Constante
Tipo especial de varivel
Valor constante

Tipos de Dados



Existem no banco de dados: Number, Char, Long, Long Raw, Raw, Rowid, Varchar2, Date.
Binary_Integer: Utilizado para armazenar inteiros com sinal. Com intervalo de:
-2147483647 .. 2147483647
Subtipos:
Natural (de 0 .. 2147483647)
Positive (de 1 .. 2147483647)
Tipos de Dados Numricos
Utilize NUMBER para armazenar nmeros
(inclusive ponto-flutuante)
Preciso Mxima: 38 dgitos
1.0E- 129 at 9.999E125
Declarao de uma varivel numrica:
NUMBER ( preciso, escala )
preciso: nmero total de dgitos
escala: nmero de dgitos a direita ou
esquerda do ponto decimal em que o
arredondamento ocorrer.

Se a escala positiva, ento a escala determina que o ponto onde o arredondamento
ocorre est a direita do ponto decimal.
Se a escala negativa, ento a escala determina que o ponto onde o arredondamento
ocorre est a esquerda do ponto decimal.
Se a escala zero, ento o arredondamento ocorre para a parte inteira do nmero.
Se a escala no especificada, ento o arredondamento no ocorre.





Caracteres
CHAR
Subtipos: Character e string.
especifica que a varivel tem um tamanho fixo pode-se especificar o tamanho mximo (1
at 32767) se o tamanho no for especificado, o valor padro 1 (um).
espaos em branco so adicionados ao final da varivel, se esta armazenar uma cadeia de
caracteres de tamanho menor que o mximo.

VARCHAR2 e VARCHAR
armazenam cadeias de caractere de tamanho varivel.
pode-se especificar o tamanho mximo (1 at 32767)
VARCHAR2 e VARCHAR so sinnimos (visando a compatibilidade com bancos de
dados ANSI ).
Recomendao da Oracle: utilize VARCHAR2

LONG
armazenam cadeias de caractere de tamanho varivel, de at 32760 caracteres.
recomendao: utilize VARCHAR2.

RAW
armazena dados binrios de at 32767 bytes o PL/SQL no tenta interpretar os dados

LONG RAW
armazena dados binrios de at 32760 bytes

Row id
No database Oracle, ROWID uma pseudocoluna, pertencente a toda tabela.
Internamente gerado, ocupando 6 bytes. ROWID em PL/SQL um subtipo do CHAR
com tamanho fixo.
BBBBBBB.RRRR.FFFF
bloco no database file, linha no bloco e
database file

Booleano (BOOLEAN)
Tipo de dados lgico (no existe correspondente no servidor Oracle). Pode assumir os
valores TRUE, FALSE ou NULL.

Data-hora (DATE)
Uma varivel do tipo DATE armazena tanto informaes sobre data quanto sobre hora.
Valor de tamanho fixo, que ocupa 7 bytes.
DATE armazena as seguintes informaes: sculo, ano, ms, dia, hora, minuto e segundo
Datas vlidas para uma varivel data esto entre 1 jan de 4712 AC a 31 dez de 4712 DC.
Podem ser feitas operaes aritmticas sobre um valor do tipo DATE.

Converso entre Tipos
Sempre que o PL/SQL efetua uma operao envolvendo um ou mais valores, ele primeiro
converte os dados para o formato correto para a operao.
Converso Explcita
usada uma funo de converso pr-definida
Converso Implcita
sempre que necessrio, o PL/SQL tenta converter os valores para efetuar a operao
Valores Nulos em PL/SQL
NULL nunca igual a qualquer outra coisa
IF nome = NULL THEN ... -- ERRADO
Quando usados uma funo utilizando um valor nulo, geralmente recebemos como
resultado um outro valor nulo.
nome := NULL;
IF LENGTH(nome) = 0 THEN -- No funciona
Maneira correta da comparao:
<identificador> IS NULL
<identificador> IS NOT NULL

Resultado de funes com argumento NULL:
Concatenao
Existem duas maneiras de efetuarmos uma concatenao: funo CONCAT ou operador | |
Nos dois casos, o valor NULL ignorado
Funo NVL
nova_desc := NVL(antiga_desc, No aplicavel );
Funo REPLACE
REPLACE( a.b.c.d.e.f. , . , NULL) = > abcdef
Declarao de Variveis
Antes de fazer qualquer referncia a uma varivel, a mesma deve ser declarada.
Sintaxe
<nome_var> <tipo> [ atribuio de valor padro]

Exemplos
data_admissao DATE;
achou BOOLEAN;
total NUMBER(15,2);
paragrafo VARCHAR2(2000);
prox_aumento CONSTANT DATE := 15-APR-96 ;
limite NUMBER DEFAULT 3;
nom_empr VARCHAR2(50) NOT NULL DEFAULT
PCS R US
Controle Condicional e Sequencial

Comandos IF...
Permitem que seja construda uma lgica condicional nas aplicaes.



Combinao IF-THEN
Exemplos
IF :empresa.total > media
THEN
aplicar_desconto(:empresa.empresa_id);
END IF;
IF relatorio_pedido
THEN
imprime_relatorio(relatorio_id);
END IF;

Exemplo
IF :cliente.total_pedido > max _permitido
THEN
pedido_excedente := TRUE;
ELSE
pedido_excedente := FALSE;
END IF;

Exemplo
IF salario < 10000
THEN
bonus:= 2000;
ELSIF salario < 20000
THEN
bonus:= 1500;
ELSIF salario < 40000
THEN
bonus:= 1000;
ELSE
bonus:= 500;
END IF;

Comando GOTO
Desvio incondicional para um rtulo definido no programa.
GOTO nome_rotulo;
...
<<nome_rotulo>>
...
Pelo menos um comando deve existir aps a definio de um rtulo.
O rtulo deve ser nico no escopo.

Rtulo destino deve estar no mesmo escopo que o comando GOTO.
IF, BEGIN, Loop, mdulo.
Rtulo destino deve estar na mesma parte do bloco PL/SQL que o comando GOTO.
ex.: um GOTO na seo executvel no pode
desviar para a seo de exceptions..
O cdigo resultante com o uso do GOTO pode tornar-se complexo e desestruturado,
dificultando a manuteno.

Comando NULL
Melhorar a clareza do programa
IF : report.selection = DETAIL
THEN
exec_detail_report ;
ELSE
NULL;
END IF;
Tirar o efeito de uma exception
Projeto top-down dos mdulos do sistema.
Utilizao conjunta com o GOTO.

SQL*Plus

Permite introduzir interativamente instrues de SQL e blocos PL/SQL a partir de uma linha
de comandos que so enviadas diretamente para a base de dados.
Comandos no so sensveis a maisculas e minsculas.
Variveis de Substituio
Identificadas pelo caracter & Substituio textual da varivel antes de enviar a instruo
para o servidor
Exemplo:
select * from emp
Where empno =&num_empregado;
Variveis de Associao
Variveis de memria, podendo ser utilizadas em um ou mais blocos PL/SQL
Tipos vlidos:
VARCHAR2
CHAR
NUMBER
No podem ser restringidas por preciso ou escala
REFCURSOR (a partir do SQL* Plus 3.2)

Exemplo:
SQL> VARIABLE v_contador NUMBER
SQL> BEGIN
2 SELECT COUNT(*)
3 INTO :v_contador
4 FROM emp
5 WHERE empno > 1000;
6 END;
7 /
SQL> PRINT v_contador
Aps a execuo, a varivel v_contador conter o resultado do select feito no bloco.

EXECUTE
Uma chamada a stored procedures deve ser feita atravs do comando EXECUTE
Exemplo:
EXECUTE minha_procedure(param1,...);
O SQL*Plus enviar o seguinte bloco PL/SQL para a base de dados:
BEGIN minha_procedure(param1,...); END;
Executar Arquivos de Instrues
Para executar quaisquer instrues SQL ou blocos PL/SQL pode-se utilizar start ou @
Exemplos:
start cria_proc.sql
start cria_func
start pacote1. pck
@funcao_teste.fnc
A extenso default sql

SHOW ERRORS
Mostra erros de compilao armazenados na view user_errors .Utilizado aps uma
tentativa de criar stored procedures e receber a mensagem:
Warning: Procedure created with compilation errors

Exerccio I.1
Faa um script que, dado um nmero inteiro, retorne o sua raiz quadrada (p/ rodar no
SQL*Plus).
Sugestes:
utilize a funo SQRT
utilize o modo de entrada de dados do SQL* Plus (&variavel)
construa um bloco annimo
utilize o comando DBMS_OUTPUT.PUT_LINE

Exerccio I.2
Faa um script que calcule as razes de uma equao de 2o grau.
Sugestes:
utilize o mdulo de entrada de dados do SQL* Plus (&variavel)
construa um bloco annimo
Obs.: eq = ax2 + bx + c = 0
raiz1 = ( - b + sqrt (b2 - 4.a.c) ) / 2.a
raiz2 = ( -b - sqrt (b2 - 4.a.c) ) / 2.a


Loops

Conceitos
Um loop permite que um mesmo cdigo seja executado repetidamente.
Loop Simples
Loop FOR (p/ nmeros e cursores)
Loop WHILE
Na maioria dos casos, uma lgica que requer um loop pode usar qualquer das trs
construes existentes.
Loop Simples
LOOP
<comandos>
END LOOP;
O teste para terminao feito dentro do loop
EXIT
EXIT WHEN <condio>



LOOP
<comandos>
END LOOP;
O teste para terminao feito dentro do loop
EXIT
EXIT WHEN <condio>




Quando usar
no existe a certeza de quantas vezes o loop ser executado
o loop deve executar pelo menos uma vez

Loop FOR
FOR numrico
FOR < indice_loop > IN [REVERSE] <menor>..<maior>
LOOP
<comandos>
END LOOP;
O loop termina quando o cdigo executado o nmero de vezes correspondente ao
intervalo informado. Aps cada execuo do bloco, o PL/SQL verifica se o valor atual do
ndice excede a diferena entre o maior e menor nmero informado na faixa.

FOR numrico
Quando usar:
cdigo dentro do loop ser executado um nmero fixo de vezes, sem ser necessria uma
interrupo
Regras
no declare o ndice usado no loop
no mude o valor das variveis usadas para informar a faixa de valores (a faixa
analisada no incio do loop), muito menos o valor do ndice
no use o comando EXIT dentro do loop FOR



Loop WHILE
WHILE <condio>
LOOP
<comandos>
END LOOP;
Executa at que a condio seja falsa.
Antes de cada execuo do bloco dentro loop,
o PL/SQL avalia a condio informada.

Quando usar
no temos certeza de quantas vezes devemos
executar o corpo do loop
desejamos interromper o loop de acordo com
uma condio
no necessariamente temos que executar o loop
pelo menos uma vez



Registros em PL/SQL
Conceitos
Um registro em PL/SQL bastante similar estrutura de linhas em uma tabela.
Um registro uma estrutura de dados composta. O registro como um todo no tem um
valor. Cada componente ou campo que o possui.
Tipos de Registro
Baseado em Tabela
cada campo corresponde a uma coluna em uma tabela, inclusive com o mesmo nome
Baseado em Cursor
cada campo corresponde a uma coluna ou expresso no comando SELECT de um
cursor
Definido pelo Programador
cada campo definido explicitamente (nome e tipo) atravs do comando TYPE
Declara o de um Registro
Baseado em Tabelas
<nome_reg> <nome_tabela>%ROWTYPE;
DECLARE
empresa_reg empresa%ROWTYPE;
Baseado em Cursores
<nome_reg> <nome_cursor>%ROWTYPE;
DECLARE
empresa_reg empresa_cur%ROWTYPE;

TYPE <nome_tipo> IS RECORD
( <nome_campo1> <tipo_dado1>,
<nome_campo2> <tipo_dado2>,
...
<nome_campoN> <tipo_dadoN> )
TYPE cliente_regtipo IS RECORD
(cliente_id NUMBER(5),
cliente_nome cliente.nome%TYPE,
total_vendas NUMBER(15,2) );

Tabelas PL/SQL
Definio
Como um array, uma tabela PL/SQL uma coleo ordenada de elementos de um mesmo
tipo. Uma tabela PL/SQL no tem limites de tamanho, pode ser incrementada
dinamicamente.
O ndice de acesso da tabela no precisa ser um nmero sequencial. Por exemplo, pode-
se usar uma srie como nmero do empregado (como 7369, 7499, 7521, 7566, )
Definindo uma Tabela PL/SQL
TYPE table_type_name IS TABLE OF
datatype [NOT NULL]
[INDEX BY BINARY_INTEGER];
onde table_type_name um tipo especificado pelo usurio.
Na verso 8 a clusula INDEX BY opcional.
Exemplos


Referenciando Tabelas PL/SQL



Usando Tabelas PL/SQL
DECLARE
TYPE DeptTabTyp IS TABLE OF dept%ROWTYPE
INDEX BY BINARY_INTEGER;
dept_tab DeptTabTyp;
BEGIN
/* Select entire row into record stored by first element. */
SELECT * INTO dept_tab(1) FROM dept WHERE deptno = 10;
IF dept_tab(1).dname = ACCOUNTING THEN ...
...
END;

DECLARE
TYPE EmpTabTyp IS TABLE OF emp%ROWTYPE
INDEX BY BINARY_INTEGER;
emp_tab EmpTabTyp;
i BINARY_INTEGER := 0;
CURSOR c1 IS SELECT * FROM emp;
BEGIN
OPEN c1;
LOOP
i := i + 1;
/* Fetch entire row into record stored by its element. */
FETCH c1 INTO emp_tab(i);
EXIT WHEN c1%NOTFOUND;
process data record
END LOOP;
CLOSE c1;
END;

Atributos de Tabela: COUNT
Retorna o nmero de elementos na tabela.
...
IF ename_tab.COUNT = 50 THEN
...
END;
Atributos de Tabela: DELETE
Este atributo tem 3 formas:
tabela.DELETE remove todos os elementos da tabela;
tabela.DELETE(n) remove o elemento n da tabela;
tabela.DELETE(m,n) remove o range m .. n.
uma instruo completa por si s; no chamada como parte de uma expresso

Atributos de Tabela:
EXISTS
Retorna TRUE se existir uma linha com ndice i na tabela, caso contrrio retorna
FALSE.
IF sal_tab.EXISTS(i) THEN
sal_tab(i) := sal_tab(i) + 500;
ELSE
RAISE salary_missing;
END IF;
...

Atributos de Tabela: FIRST e LAST
Retornam o ndice da primeira e da ltima linha da tabela, respectivamente.
...
FOR i IN emp_tab.FIRST .. emp_tab.LAST LOOP
...
END LOOP;
A primeira linha a que tem o ndice mais baixo e a ltima, o mais elevado.

Atribut os de Tabela: NEXT e PRIOR
Retornam o ndice do elemento seguinte ou anterior da tabela, respectivamente.
DECLARE
...
i BINARY_INTEGER;
BEGIN
..
i := any_tab.FIRST;
WHILE i IS NOT NULL LOOP
... process any_tab(i)
i := any_tab.NEXT(i);
END LOOP;
END;

SQL em PL/SQL
Instrues de SQL
Podem dividir-se em seis categorias:
DML: linguagem de manipulao de dados
DDL: linguagem de definio de dados
Controle de transaes
Controle de sesses
Controle do sistema
SQL incorporado (para pr- compiladores)

Num programa PL/SQL s podem ser usadas as seguintes instrues:
DML: select, insert, update, delete
Controle de transaes: commit, rollback, savepoint

Existe uma alternativa para o uso de instrues DDL em PL/SQL
A package DBMS_SQL, disponvel a partir da verso 2.1 permite a criao de SQL
dinmico
DML: Select
Busca informaes do banco de dados para variveis PL/SQL
SELECT emp.ename
INTO v_ename
FROM emp
WHERE emp.empno = 7902;
Deve retornar somente uma linha. A clusula INTO s usada em blocos PL/SQL
DML: Insert
Insere uma linha na tabela
INSERT INTO emp
(empno, ename, job, mgr, hiredate, sal, comm,
deptno)
VALUES
(1, 'ALBERT', 'SALESMAN', 7698, SYSDATE,
1432, 260, 30);
INSERT INTO emp
SELECT * FROM emp;
DML: Update
Altera o contedo de uma ou mais linhas de uma tabela
UPDATE emp
SET sal = sal * 1.2
WHERE empno = 1;
DML: Delete
Elimina uma ou mais linhas de uma tabela
DELETE FROM emp
WHERE empno = 1;
Clusula WHERE
Nas instrues SELECT, UPDATE e
DELETE esta clusula serve para restringir o conjunto de linhas sobre as quais a operao
ser executada. Constituda por condies, normalmente de comparao, separadas pelos
operadores AND, OR, NOT.
Referncias de tabelas
As operaes de DML referenciam uma tabela, de uma forma geral, no formato:
[owner.] tabela[@dblink] onde:
owner - usurio onde a tabela foi criada
dblink - denominao de uma conexo a um banco de dados remoto
Sinnimos
Utilizados para facilitar o acesso aos objetos do banco de dados, evitando o uso de owner
e dblink para identific-los.
Exemplo:
CREATE PUBLIC SYNONYM empregado FOR owner1.emp@bd1;
Aps isto pode-se usar:
SELECT * FROM empregado;
em vez de ter que fazer:
SELECT * FROM owner1.emp@bd1;
Pseudo-colunas
Funes adicionais que s podem ser chamadas a partir de instrues SQL:
CURRVAL e NEXTVAL
LEVEL
ROWID
ROWNUM
Pseudo-colunas: CURRVAL e NEXTVAL
Utilizadas com sequncias (objetos Oracle para gerar nmeros nicos)
sequncia.CURRVAL
retorna o valor atual da sequncia
sequncia.NEXTVAL
retorna o prximo valor da sequncia
Exemplo:
SELECT my_seq.NEXTVAL from dual;

Pseudo-colunas: LEVEL
Utilizado dentro de uma instruo SELECT que implementa uma pesquisa de rvore
hierrquica numa tabela utilizando as clusulas START WITH e CONNECT BY.
Esta pseudo-coluna retorna o nvel atual da rvore.

Pseudo-colunas: ROWID
Retorna o endereo fsico de uma linha da tabela, num valor do tipo ROWID
Um SELECT utilizando-se de ROWID na clusula WHERE o mtodo mais otimizado de
se recuperar uma linha
Exemplo:
v_rowid ROWID;
...
SELECT ROWID INTO v_rowid FROM emp;
...
UPDATE emp set ... WHERE emp.rowid = v_rowid;

Pseudo-colunas: ROWNUM
Retorna o nmero atual da linha num SELECT. Utilizado principalmente na clusula
WHERE para limitar as linhas a serem consideradas.
SELECT * FROM emp WHERE ROWNUM < 3;
O valor ROWNUM atribudo a uma linha antes de ser efetuada uma ordenao (ORDER
BY)

Privilgios de Acesso

Privilgios de Objeto
Para efetuar operaes num objeto
Privilgios de Sistema
Para efetuar operaes numa classe de objetos GRANT
Para dar privilgios de acesso
Objeto:
GRANT privilgio ON objeto TO usurio
[WITH GRANT OPTION]
Sistema:
GRANT privilgio TO usurio
[WITH ADMIN OPTION]
REVOKE
Para revogar privilgios de acesso
Objeto:
REVOKE privilgio ON objeto FROM usurio
[CASCADE CONSTRAINTS]
Sistema:
REVOKE privilgio FROM usurio
Perfis de Grupo
Os privilgios comuns podem ser agrupados em ROLES, para facilitar a concesso para
vrios usurios que possuem o mesmo perfil. Em vez de dar privilgios para cada usurio:
Cria-se uma role: CREATE ROLE role
Concedem-se os privilgios: GRANT ... TO role
Atribuem-se os usurios para essa role GRANT role TO usurio
PUBLIC: perfil genrico para todos os usurios

Controle de Transaes
COMMIT
Salva as operaes da transao e libera locks. As operaes so visveis a outras
sesses
ROLLBACK
Desfaz as operaes e libera locks
SAVEPOINT
Ponteiro para marcar o nicio para onde um ROLLBACK pode ser efetuado

Funes de SQL integradas
Funes para Caracteres





Funes para Caracteres
Exemplos
FUNCTION INSTR(string1 IN VARCHAR2,
string2 IN VARCHAR2
[ , pos_ini IN NUMBER := 1
[ , nth_ocorrencia IN NUMBER := 1] ] )
RETURN NUMBER
INSTR( Estou procurando uma palavra , uma ) => 18
FUNCTION LPAD(string1 IN VARCHAR2,
tamanho_pad IN NUMBER
[ , string_pad IN VARCHAR2] )
RETURN VARCHAR2
LPAD( 55 , 10, 0) = > 0000000055
LPAD( 12345678 , 5, 0 ) = > 12345

Exemplos
FUNCTION LTRIM(string1 IN VARCHAR2
[ , trim_string IN VARCHAR2] )
RETURN VARCHAR2
LTRIM( Eu gosto de pizza ) => Eu gosto de pizza
FUNCTION SUBSTR(string_in IN VARCHAR2,
pos_ini IN NUMBER
[ , tam_substr_in IN
NUMBER] )
RETURN VARCHAR2
SUBSTR( Eu gosto de pizza , 4, 5) => gosto
SUBSTR( Eu gosto de pizza , -1) => a

Funes para Datas



Exemplos
LAST_DAY(data_in IN DATE) RETURN DATE
LAST_DAY(SYSDATE) - SYSDATE => nmero de dias at o final do ms.
NEXT_DAY(data_in IN DATE, nome_dia IN VARCHAR2)
RETURN DATE
NEXT_DAY( 01-JAN-1997 , MONDAY) => 06-JAN-1997
Funes Numricas





Outras Funes



Exemplos
FUNCTION SQLCODE RETURN INTEGER
FUNCTION SQLERRM RETURN VARCHAR2
EXCEPTION
WHEN OTHERS THEN
MESSAGE( Error | | TO_CHAR(SQLCODE) | | : | | SQLERRM);


Funes de Converso



Funo TO_CHAR
FUNCTION TO_CHAR(param IN {DATE/NUMBER} ,
[ , formato IN VARCHAR2
[ , nls_language IN
VARCHAR2] ] )
RETURN VARCHAR2
TO_CHAR(SYSDATE, Month DD, YYYY) =>March 10,1997
TO_CHAR(564.70, $999.9 ) => $564.7
TO_CHAR(564.70, $0000999.9 ) => $0000564.7
Funo TO_DATE
FUNCTION TO_DATE(param IN {VARCHAR2|NUMBER}
[ , formato IN VARCHAR2
[ , nls_language IN VARCHAR2 ] ] )
RETURN DATE
TO_DATE( 123198 , MMDDYY) => 31- DEC-1998
TO_DATE( 16/7/94 , DD/MM/YY) = > 16- JUL-1994
Funo TO_NUMBER
FUNCTION TO_NUMBER(string_in IN
CHAR|VARCHAR2}
[ , formato IN VARCHAR2
[ , nls_language VARCHAR2] ] )
RETURN NUMBER
TO_NUMBER( 123.23 ) => 123.23
TO_NUMBER( abcdef ) => ERRO

Funes de Grupo
Somente para SELECT com GROUP BY



Cursores
Conceitos
Um cursor pode ser encarado como um ponteiro para a tabela virtual no banco de dados
representada pelo comando SELECT associado.
Ex.:
CURSOR empregado_cur IS
SELECT * FROM empregado;
OPEN empregado_cur;
FETCH empregado_cur INTO empregado_rec;
CLOSE empregado_cur;

Operaes em Cursores
OPEN
abre o cursor, faz o parse e o bind da consulta associada, identificando o resultado
o cursor posicionado antes da primeira linha.
FETCH
busca a linha corrente do cursor e o posiciona na prxima
CLOSE
fecha o cursor e libera a memria alocada.
Tipos de Cursores
Cursores Estticos
sempre referenciam um comando SQL, que conhecido em tempo de compilao.
Variveis Cursores
a varivel referencia um objeto cursor e pode referenciar diferentes comandos SQL em
ocasies diferentes. Nova feature do PL/SQL.

Implcitos
o PL/SQL utiliza um cursor implcito sempre que um comandos SQL executado
diretamente no cdigo, desde que o cdigo no utilize um cursor explcito usados em cada
UPDATE, INSERT ou DELETE so menos eficientes que cursores explcitos
mais suscetveis a erro.
Explcitos
comando SELECT explicitamente definido na seo de declarao, sendo um nome
associado a ele usados quando desejamos recuperar mais de uma linha de resultado
no existem para comandos de UPDATE, INSERT e DELETE
Declarao
CURSOR nome_cursor [ ( [ parmetro [ ,
parmetro ...] ) ]
[ RETURN especificao_retorno ]
IS comando_SELECT;
Variveis em um Cursor
Nome do Cursor no uma varivel. No PL/SQL, a lista de itens no SELECT pode
conter colunas, variveis do PL/SQL e variveis associadas (p.e. Oracle Forms).
DECLARE
bonus NUMBER := 100;
CURSOR empregado_cur IS
SELECT empregado_id, salario + bonus,
:revisao.avaliacao
FROM empregado
WHERE dt_contrat < ADD_MONTHS(SYSDATE, -36);
Precedncia de um Identificador
Em um cursor, existe precedncia do nome da coluna sobre o nome de uma varivel
PL/SQL.
PROCEDURE melhorar_SQL
IS salario NUMBER := 1000;
CURSOR dobrar_sal_cur IS
SELECT salario + salario
FROM empregado
WHERE dt_contrat < ADD_MONTHS(SYSDATE,- 36);
BEGIN
A clasula RETURN
Somente pode ser usada para cursores que esto contidos em um package.
A clasula RETURN pode ser feita com as seguintes estruturas:
Um record definido a partir de uma tabela,
usando %ROWTYPE
Um record definido a partir de um record pr-definido pelo progamador
Exemplo de Uso da clasula RETURN
Primeiro a definio da Package
PACKAGE empresa IS
CURSOR empresa_cur (id IN NUMBER)
RETURN empresa%ROWTYPE;
END empresa;
Depois a definio da Package Body
PACKAGE body empresa IS
CURSOR empresa_cur (id IN NUMBER)
RETURN empresa%ROWTYPE IS
SELECT * FROM empresa
WHERE empresa_id = id;
END empresa;
Porque colocar cursor numa Package?
Uma package uma coleo de objetos logicamente relacionados. Agrupando os cdigos,
torna-se mais fcil a identificao e uso dos cdigos pelo programador. Cursores em
packages so essencialmente caixas pretas. O programador no precisa saber como um
cursor recuperado.

Abrindo Cursores
OPEN <nome_cursor> [ (parmetro [ , parmetro ...] ) ] ;
O comando OPEN no recupera linhas. Todos os fetches refletiro os dados exatamente
como da ocasio em que o cursor foi aberto. Recuperando Dados de Cursores
FETCH <nome_cursor> INTO <registro_ou_lista_variveis>
Exemplo:
FETCH empresa_cur INTO empresa_reg;
FETCH max_sal_cur INTO max_sal;
FETCH empr_cur INTO empr_nome(1),
dt_contrat,
:depto.min_ salario;

Fechando Cursores
CLOSE <nome_cursor>
Libera rea de memria (SGA). Libera todo bloqueio (lock) causado pelo cursor. Existe um
limite mximo de cursores que podem ser abertos no SGBD Oracle.
Um cursor automaticamente fechado quando o seu escopo abandonado.

Atributos de Cursores



%FOUND
OPEN pedido_cur;
FETCH pedido_cur INTO pedido_id, empresa_id;
IF pedido_cur %FOUND THEN
:pedido.num_pedidos := :pedido.num_pedidos + 1;
END IF;
...

%NOTFOUND
Oposto ao %FOUND
Muito utilizado para terminao de loops
EXIT WHEN empresa_cur%NOTFOUND;

%ROWCOUNT
DECLARE
CURSOR emp_cur IS
SELECT nome, salrio FROM empregado
ORDER BY salario DESC;
emp_reg emp_cur %ROWTYPE;
BEGIN
OPEN emp_cur;
LOOP
FETCH emp_ cur INTO emp_reg;
EXIT WHEN emp_cur %ROWCOUNT > 10 OR
emp_cur %NOTFOUND;
DBMS_OUTPUT.PUT_LINE(emp_reg.nome || - ||
emp_reg.salario);
END LOOP;
END;

%ISOPEN
IF NOT emp_cur %ISOPEN THEN
OPEN emp_cur;
END IF;

Atributos de Cursores Implcitos
Quando o RDBMS abre um cursor ou executa um insert, update ou delete, ele
torna uma das seguintes variveis habilitadas:
SQL%FOUND
SQL%NOTFOUND
SQL%ROWCOUNT
SQL%ISOPEN

Parmetros em Cursores
Um parmetro faz com que um cursor se torne mais reutilizvel.
DECLARE
CURSOR empresa_cur (categoria_in VARCHAR2)
IS
SELECT nome, categoria, contato
FROM empresa
WHERE categoria = UPPER(categoria_in);
Podemos definir um valor padro para um parmetro.
CURSOR emp_cur(emp_in NUMBER := 0)
SELECT ... FOR UPDATE
Quando um comando SELECT ... FOR UPDATE executado, o Oracle automaticamente
bloqueia a linha de maneira exclusiva. Ningum conseguir alterar estes registros antes de
um ROLLBACK ou COMMIT
CURSOR emp_cur IS
SELECT nome, salario FROM empregado
WHERE salario < 100
FOR UPDATE
WHERE CURRENT OF
Esta clusula utilizada para comandos.
DELETE e UPDATE dentro de um cursor.
O registro mais recentemente recuperado apagado ou atualizado
Exemplo:
FETCH emp_cur INTO emp_reg;
...
UPDATE empregado SET salario := salario + bonus
WHERE CURRENT_OF emp_cur;

Variveis Cursores
Disponvel a partir das releases 2.2 e 2.3. Possibilidade de passar como parmetro o
resultado de consultas para outros programas. Variveis cursores so como ponteiros do C
ou Pascal, na qual um endereo de memria assinalado. Declarando uma
varivel cursor se cria um ponteiro, no um objeto.
Porque usar variveis cursor?
Usa-se uma varivel cursor para passar o resultado de uma query entre stored
subprograms e aplicaes client. Por exemplo, uma aplicao Client, Oracle
Forms, e Oracle Server podem ambos se referenciar mesma rea de trabalho.


Exemplo
DECLARE
TYPE empresa_curtipo IS REF CURSOR
RETURN empresa%ROWTYPE;
empresa_curvar empresa_curtipo;
BEGIN
OPEN empresa_curvar FOR SELECT *
FROM empresa;
...
Definindo uma Varivel Cursor
TYPE ref_type_name IS REF CURSOR
RETURN return_type;
onde ref_type_name o nome da varivel especificada para uso subsequente e
return_type deve representar um record ou uma row na tabela.
Exemplo:
DECLARE
TYPE DeptCurTyp IS REF CURSOR
RETURN dept%ROWTYPE;
Em uma Stored Procedure
CREATE PACKAGE emp_data AS
TYPE GenericCurTyp IS REF CURSOR;
TYPE EmpCurTyp IS REF CURSOR
RETURN emp%ROWTYPE;
PROCEDURE open_emp_cv
(emp_cv IN OUT EmpCurTyp,
choice IN NUMBER);
END emp_data;

CREATE PACKAGE BODY emp_data AS
PROCEDURE open_emp_cv
(emp_cv IN OUT EmpCurTyp,
choice IN NUMBER) IS
BEGIN
IF choice = 1 THEN
OPEN emp_cv FOR SELECT * FROM emp
WHERE comm IS NOT
NULL;
ELSIF choice = 2 THEN
OPEN emp_cv FOR SELECT * FROM emp
WHERE sal > 2500;
ELSIF choice = 3 THEN
OPEN emp_cv FOR SELECT * FROM emp
WHERE deptno = 20;
END IF;
END open_emp_cv;
END emp_data;
Loop Simples para Cursores
As instrues devem ser feitas explicitamente:
Abrir o cursor
Colocar o fetch dentro do loop
Estabelecer a condio para fim do loop
Fechar o cursor
Requer maior ateno do desenvolvedor. Maior possibilidade de ocorrer erro

Exemplo:
DECLARE
CURSOR cur_emp IS ...
BEGIN
OPEN cur_emp;
LOOP
FETCH cur_emp INTO ...
EXIT WHEN cur_emp%NOTFOUND;
-- processar informaes do cursor
END LOOP;
CLOSE cur_emp;
END;
Loop FOR para Cursores
FOR indice_registro IN nome_cursor
LOOP
<comandos>
END LOOP;
O loop termina incondicionalmente quando todos os registros do cursor forem
recuperados. A cada execuo do loop, o PL/SQL verifica o atributo %NOTFOUND

Exemplo
DECLARE
CURSOR ocupacao_cur IS
SELECT hosp_id, nm_quarto
FROM ocupacao WHERE dt_ocupacao = SYSDATE;
BEGIN
FOR ocupacao_reg IN ocupacao_cur
LOOP
atualiza_nota(ocupacao_reg.hosp_id,
ocupacao_reg.nm_quarto);
END LOOP;
END;

Quando usar
Quando desejamos recuperar e processar todos os registros do cursor no apropriado
situaes em que condies devem ser avaliadas para determinar o trmino da operao
de recuperao.
Observao: O ndice do loop, neste caso uma varivel do tipo registro, encarado da
mesma forma que ndices numricos.

Exerccio II .1
Liste os 3 departamentos com maior folha (soma dos salrios dos empregados), em
ordem decrescente.
Sugesto:
Utilize as tabelas dept e emp (SCOTT/TIGER)
utilize o procedimento
DBMS_OUTPUT.PUT_LINE

Exerccio II .2
De acordo com o salrio de um empregado, calcule o imposto de renda correspondente.
Sugestes
Utilize a tabela emp SCOTT/TIGER
No considere possveis dedues
Obs.:
salario < 900 - isento
salario > 900 e <= 1800 - 15% - R$135
salario > 1800 - 25% - R$315

Exerccio II .3
Calcular o total de salrios pagos (salrio + comisso) para empregados de um
departamento. Determinar tambm quantos empregados tem salrio maior
que $2000 e quantos tem a comisso maior que o salrio.
Subprogramas:

Procedimentos e Funes
Conceitos
Modularizao
Processo de quebrar grandes blocos de cdigo em pequenos pedaos (mdulos)
torna o cdigo:
mais reutilizvel
mais fcil de gerenciar
mais legvel
mais confivel

Estruturas para Modularizao (PL/SQL):
Procedimento
Bloco que efetua uma ou mais aes, sendo possvel a passagem de informaes, tanto
para dentro quanto para fora do procedimento
Funo
Retorna um nico valor; podemos passar informaes para a funo atravs de
parmetros
Bloco annimo
Bloco PL/SQL que efetua uma ou mais tarefas;
usado para controlar o escopo dos
identificadores e manuseio de exceptions
Package
coleo de procedimentos, funes, tipos e variveis;
no exatamente um mdulo, mas est relacionado ao assunto.

Estrutura do Bloco PL/SQL (Reviso)




Estrutura de Blocos PL/SQL




Procedimentos
Estrutura
PROCEDURE <nome> [ (parmetro [ ,parmetro ...] ) ] IS
[comandos de declarao]
BEGIN
<comandos>
[ EXCEPTION <comandos para manuseio de exceptions> ]
END [nome] ;

Chamada
um procedimento chamado da mesma maneira que um comando PL/SQL
aplicar_desconto(nova_empr_id, 15.00);


Cabealho
nome do procedimento e lista de parmetros
PROCEDURE aplicar_desconto(empr_id_in IN empresa.empr_id%TYPE, desconto_in IN
NUMBER)
Corpo
cdigo necessrio para a execuo do procedimento
PROCEDURE nada IS
BEGIN
NULL;
END;
Exemplo de Procedure
PROCEDURE raise_salary (emp_id INTEGER, increase REAL) IS
current_salary REAL;
salary_missing EXCEPTION;
BEGIN
SELECT sal INTO current_salary FROM emp
WHERE empno = emp_id;
IF current_salary IS NULL THEN
RAISE salary_missing;
ELSE
UPDATE emp SET sal = sal + increase
WHERE empno = emp_id;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO emp_audit VALUES (emp_id, No such number );
WHEN salary_missing THEN
INSERT INTO emp_audit VALUES (emp_id, Salary is null );
END raise_salary;

Funes
Estrutura
FUNCTION nome [ (parmetro [ , parmetro...] ) ]
RETURN tipo_retornado IS [comandos de declarao]
BEGIN
comandos
[ EXCEPTION comandos para manuseio de exceptions]
END [nome]

Chamada
uma funo chamada como parte de um comando, sempre que uma expresso pode ser
usada
vendas_95 := total_vendas( Marisol , 1995);
DECLARE
vendas_95 NUMBER DEFAULT
total_vendas( Marisol , 1995);
IF total_vendas( Marisol , 1995)
THEN ...

Cabealho
nome da funo, lista de parmetros e tipo do retorno
FUNCTION total_vendas(nome_in IN
empresa.nome%TYPE, ano_in pedido.ano%TYPE)
RETURN NUMBER;

Corpo
cdigo necessrio para a execuo da funo
FUNCTION nada RETURN BOOLEAN IS
BEGIN
RETURN TRUE;
END;

Declarao RETURN
Encerra a execuo da funo e retorna o valor para o programa que a chamou.
Um subprograma pode conter vrias declaraes RETURN. Em procedures a declarao
RETURN no deve conter uma expresso. Em funes a declarao RETURN deve conter
uma expresso que executada no momento da execuo da declarao.
Exemplo de Funo
FUNCTION sal_ok (salary REAL, title REAL) RETURN BOOLEAN IS
min_sal REAL;
max_sal REAL;
BEGIN
SELECT losal, hisal INTO min_sal, max_sal
FROM sals
WHERE job = title;
RETURN (salary >= min_sal) AND (salary <= max_sal);
END sal_ok;
Parmetros
Modo de troca de informaes entre o mdulo e o bloco PL/SQL que o chamou.
Quando declaramos um parmetro, nunca especificamos restries quanto ao
tamanho do tipo de dado.
PROCEDURE mostra_empresa(nome IN VARCHAR2) IS
permitida a utilizao de %TYPE e %ROWTYPE na declarao de parmetros
Modo de Passagem de Parmetros
IN
somente para leitura
OUT
somente para escrita (o mdulo pode atribuir um valor ao parmetro, que ser passado ao
bloco PL/SQL que o chamou)
IN OUT
usado para leitura e escrita
Passagem de Parmetros
PROCEDURE combine_formate_nomes (prim_nome IN OUT VARCHAR2,
ult_nome IN OUT VARCHAR2,
nome_comp OUT VARCHAR2,
formato IN VARCHAR2 := ULTIMO PRIMEIRO)
-- O parmetro formato no precisa obrigatoriamente ser informado
IS
BEGIN
IF formato = ULTIMO PRIMEIRO THEN
nome_comp := ult_nome | | , | | prim_nome
ELSIF formato = PRIMEIRO ULTIMO THEN
nome_comp := prim_nome | | | | ult_nome;
END IF;
END;

Como o PL/SQL faz a associao dos parmetros?
Notao Posicional
associa o valor ao parmetro correspondente implicitamente atravs da posio
Notao por Nome Explicitamente
associa um valor a um parmetro explicitamente atravs do seu nome
vendas_97 := total_vendas(nome_in => Cia. JK,
ano_in => 1997);
vendas_97 := total_vendas(ano_in => 1997,
nome_in => Cia. JK);
Stored Procedures/Functions
Uma stored procedure ou stored function uma unidade de programa PL/SQL que:
tem um nome
pode receber e retornar valores
fica armazenada no dicionrio de dados
pode ser usada por vrios usurios

CREATE PROCEDURE recupera_emp_reg
(emp_numero IN emp.empno%TYPE,
emp_reg OUT emp%ROWTYPE) AS
BEGIN
SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno
INTO emp_ret
FROM emp
WHERE empno = emp_numero;
END;

Exemplo:
Mdulo Local
Procedimento ou funo definido da seo de declarao de um bloco PL/SQL.
No pode ser chamado por nenhum outro mdulo PL/SQL definido fora do bloco ao
qual o mesmo pertence.
Vantagens
reduz o tamanho do mdulo, eliminando
cdigos repetitivos
torna o cdigo mais legvel

DECLARE
rent REAL;
PROCEDURE raise_rent (increase IN OUT REAL) IS
BEGIN
rent := rent + increase;
...
END raise_rent;
...
BEGIN
...
raise_rent(rent); indeterminate

Dependncias dos Subprogramas
Subprogramas so dependentes dos objetos que referenciam.Caso algum destes objetos
forem alterados por uma operao DDL, o subprograma fica com status INVALID e deve
ser recompilado.
ALTER {PROCEDURE | FUNCTION} nome COMPILE;
Determinao de dependncia (no INIT.ORA)
Timestamp
Assinatura

Privilgios de Acesso
Para poder executar um subprograma necessrio ter o privilgio EXECUTE para
o objeto. Um subprograma executado utilizando os privilgios explcitos do seu owner,
sem considerar privilgios de roles.

Packages
Conceitos
Uma package um conjunto de objetos PL/SQL que so empacotados com uma
sintaxe especial de BEGIN-END. Podemos colocar em uma package:
cursores
variveis nomes de exceptions
comandos TYPE
procedimentos e funes

Vantagens de utilizarmos packages:
information hiding
projeto orientado a objetos
projeto top-down
persistncia de objetos
melhor performance

Interface



Estrutura de uma Package
Especificao
contm as definies e especificaes de todos os elementos em uma package que
podem ser referenciados fora dela.
PACKAGE nome_package IS
[ declaraes de variveis e tipos ]
[ declaraes de cursores ]
[ declaraes de mdulos ]
END [nome_package] ;

Corpo
contm implementao de mdulos, cursores e outros objetos
PACKAGE BODY nome_package IS
[ declaraes de variveis e tipos ]
[ especificaao de comandos SELECT de cursores ]
[ especificao do corpo de mdulos ]
[ BEGIN
comandos executveis ]
[ EXCEPTION
exception handlers ]
END [nome_package] ;
Exemplo Package - Especificao
CREATE PACKAGE emp_actions AS specification
TYPE EmpRecTyp IS RECORD (emp_id INTEGER, salary REAL);
CURSOR desc_salary RETURN EmpRecTyp;
PROCEDURE hire_employee (ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER);
PROCEDURE fire_employee (emp_id NUMBER);
END emp_actions;

Exemplo Package - Body
CREATE PACKAGE BODY emp_actions AS
CURSOR desc_salary RETURN EmpRecTyp IS
SELECT empno, sal FROM emp ORDER BY sal DESC;
PROCEDURE hire_employee (ename VARCHAR2,
job VARCHAR2,
mgr NUMBER,
sal NUMBER,
comm NUMBER,
deptno NUMBER) IS
BEGIN
INSERT INTO emp VALUES (empno_seq.NEXTVAL, ename, job,
mgr, SYSDATE, sal, comm, deptno);
END hire_employee;
PROCEDURE fire_employee (emp_id NUMBER)
IS
BEGIN
DELETE FROM emp WHERE empno = emp_id;
END fire_employee;
END emp_actions;

Packages e Escopo
Objetos declarados na Package Specification tm escopo pblico, podendo ser utilizados
fora da package.
nome_da_package.nome_do_subprograma (...)
nome_da_package.nome_do_tipo
Aqueles objetos definidos somente no Package Body tm escopo privado,
podendo somente ser utilizados por outros objetos dentro da package
Over loading de Mdulos
Dois ou mais mdulos podem ter o mesmo nome com uma lista diferente de parmetros.


Onde Fazer?
na seo de declarao de um bloco PL/SQL dentro de um package

No podemos fazer a sobrecarga de nomes de programas independentes,
muito menos criar dois mdulos independentes com o mesmo nome e
listas de parmetros distintas.
Exemplo:
DECLARE
TYPE DateTabTyp IS TABLE OF DATE
INDEX BY BINARY_INTEGER;
TYPE RealTabTyp IS TABLE OF REAL
INDEX BY BINARY_INTEGER;
hiredate_tab DateTabTyp;
comm_tab RealTabTyp;
indx BINARY_INTEGER;
...
BEGIN
indx := 50;
initialize(hiredate_tab, indx); calls first version
initialize(comm_tab, indx); calls second version
...
END;

Inicializao de Packages
Uma package pode conter um conjunto de instrues a serem executadas somente quando
a mesma carregada para a memria.
CREATE OR REPLACE PACKAGE BODY nome_pack AS
...
BEGIN
-- Cdigo de inicializao
...
END nome_pack;

Dependncias de Packages
O Package Body depende da Package Specification e dos objetos referenciados
A Package Specification no depende de nada
Vises para dependncias no PL/SQL 8.0:
user_dependencies
all_dependencies
dba_dependencies
Utilizao de Stored Functions em instrues SQL
Uma funo independente ou contida numa package pode ser chamada numa
instruo SQL, dependendo das restries de referncia
Restries de Referncia: definem quais tipos de estruturas de dados que a funo
l ou modifica
WNDS (Writes No Database State)
RNDS (Reads No Database State)
WNPS (Writes No Package State)
RNPS (Reads No Package State)

Somente para Stored Functions Parmetros
devem ser somente de entrada
no podem utilizar tipos PL/SQL (boolean, record)
Tipo de retorno da funo tambm tem que ser um tipo da base de dados
RESTRICT_REFERENCES
Para funes independentes o PL/SQL consegue determinar as restries de
referncia.
Para funes em packages necessrio discriminar as restries atravs da pragma
abaixo. Isto porque os blocos PL/SQL que chamam uma funo empacotada dependem
apenas da package specification e no do body.
PRAGMA RESTRICT_REFERENCES (nome_funo, WNDS
[,WNPS] [,RNDS] [,RNPS]);
Algumas Packages Pr-Definidas



Exerccio III.1
Converta o script criado no exerccio I.2 (razes de uma equao do 2o grau) para
um procedimento. Armazene este procedimento no banco.

Exerccio III.2
Converta o script do exerccio II.1 (trs departamentos com maior folha - soma de
salrios) para um procedimento. Ao invs de mostrar as informaes na
tela, insira-as em uma tabela. Armazene este procedimento no banco
(stored procedure).
Exerccio III.3
Converta o script do exerccio II.2 (clculo do imposto renda) para uma funo, que
receba como parmetro a matrcula do funcionrio e retorne o imposto a ser
pago. Armazene esta funo no banco (stored function).

Exerccio III.4
Construa uma package contendo as funes / procedimentos do departamento pessoal.
Armazene esta package no banco de dados Oracle.
Salrio Lquido = SAL+COMM - Imposto de Renda
COMM = Se o JOB= CLERK, comisso =
1.03 * SAL
Gravar em Contra-Cheque (salrio normal,comisso, imposto de renda e salrio Lquido)

Triggers
Correspondem a stored procedures, com a diferena que os triggers so disparados
automaticamente quando houver operaes de insert, update e delete nas tabelas
associadas.

As aplicaes de triggers incluem:
clculo automtico de colunas
crtica de transaes
garantia de regras de segurana complexas
garantia de integridade referencial em bancos de dados distribudos
implementao de regras de negcio complexas
Sintaxe
CREATE OR REPLACE schema.trigger
[BEFORE|AFTER] DELETE OR INSERT OR UPDATE [OF
column]
ON schema.table
[REFERENCING OLD AS old NEW AS new]
FOR EACH ROW
WHEN (condition)
pl_sql_block

Triggering Statement
a especificao da ao que levar ao acionamento do trigger, podendo ser:
INSERT
UPDATE
DELETE

Trigger Restriction
uma expresso que limita a execuo de um trigger.
Deve resultar em valor TRUE ou FALSE. O trigger somente ser executado para
valores TRUE.

Trigger Action
um bloco PL/SQL que ser executado quando o trigger for acionado.

Tipos de Triggers
ROW TRIGGERS
so executados uma vez para cada linha da tabela afetada pelo comando SQL.
STATEMENT TRIGGERS
so executados apenas uma vez para cada comando SQL que afete a tabelas,
independentemente do nmero de linhas envolvidas.

Acionamento de Triggers
Quando definimos um trigger, podemos especificar quando ele ser acionado:
before row
before statement
after row
after statement

Triggers (Exemplo)
CREATE TRIGGER dummy
BEFORE DELETE OR INSERT OR UPDATE ON emp
FOR EACH ROW
WHEN (new.empno > 0)
DECLARE
/ * variveis, constantes, cursores, etc. * /
BEGIN
/ * bloco PL/SQL * /
END;

Limite de Triggers por Tabela
Um de cada tipo, totalizando at 12 triggers. No entanto no PL/SQL 2.1 uma tabela pode
ter mais de um trigger de cada tipo.
BEFORE UPDATE row AFTER UPDATE row
BEFORE DELETE row AFTER DELETE row
BEFORE INSERT statement AFTER INSERT statement
BEFORE INSERT row AFTER INSERT row
BEFORE UPDATE statement AFTER UPDATE statement
BEFORE DELETE statement AFTER DELETE statement.

Triggers instead-of
Em PL/SQL 8.0, podem ser definidos triggers que sero disparados em vez da instruo
DML que os disparou. Somente para vises a nvel de linha.
Exemplo:
Para eliminar linhas de uma view complexa
Pseudo-regist ros
Disponvel para triggers a nvel de linha
:old
valores originais do registro da tabela
somente para leitura no corpo do trigger
:new
valores do registro que sero inseridos ou
atualizados na base de dados
podem ser atribudos valores (somente
quando before)
Clusula WHEN
Vlida para triggers a nvel de linha. O corpo do trigger ser executado para as linhas que a
condio especificada resultar em TRUE. Os pseudo-registros :old e :new podem ser
utilizados dentro da condio, mas os dois pontos devem ser suprimidos

Predicados de Trigger
Usados em triggers disparados para diferentes tipos de instrues de DML
Funes booleanas que identificam a instruo que disparou o trigger
INSERTING
UPDATING
DELETING

Tratamento de Erros

Conceitos
Uma exception uma situao que no deveria ter ocorrido.
Pode ser causada por:
erro gerado pelo sistema (p.e. out of memory )
erro causado por uma ao do usurio aviso gerado pela aplicao e direcionado ao
usurio

Exception Handlers
Este mecanismo permite separar o cdigo de processamento de erros do resto dos
comandos. Oferece um modelo orientado a eventos. No importa como uma exception foi
gerada, ela ser tratada na mesma seo (exception section).
Desvio do Fluxo
PROCEDURE fluxo
IS
novo_valor VARCHAR2(5);
BEGIN


END;

Tipos de Exceptions
Existem quatro tipos de exceptions:
exceptions do sistema com um nome, geradas devido a um erro no processamento do
SGBD ou do cdigo PL/SQL, definidas pelo programador com um nome geradas devido a
um erro no cdigo; elas so declaradas na seo correspondente

Exceptions do sistema sem um nome
geradas devido a um erro no processamento do SGBD ou do cdigo PL/SQL; somente as
mais comuns possuem um nome. Definidas pelo programador sem um nome.
procedimento RAISE_APPLICATION_ERROR executado
o programador informa um nmero (-20000 a 20999) e uma mensagem de erro;
utilizada para facilitar a comunicao de erros do ambiente cliente-servidor.

Exceptions do sistema com um nome
DECLARE
stmt INTEGER := 1; designates 1st SELECT statement
BEGIN
SELECT ...
stmt := 2; designates 2nd SELECT statement
SELECT ...
stmt := 3; designates 3rd SELECT statement
SELECT ...
...
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO errors VALUES ( Error in statement || stmt);
...
END;

Exceptions Pr-Definidas
Exception Name Oracle Error SQLCODE Value
CURSOR_ALREADY_OPEN ORA 06511 6511
DUP_VAL_ON_INDEX ORA 00001 1
INVALID_CURSOR ORA 01001 1001
INVALID_NUMBER ORA 01722 1722
LOGIN_DENIED ORA 01017 1017
NO_DATA_FOUND ORA 01403 +100
NOT_LOGGED_ON ORA 01012 1012
PROGRAM_ERROR ORA 06501 6501
ROWTYPE_MISMATCH ORA 06504 6504
STORAGE_ERROR ORA 06500 6500
TIMEOUT_ON_RESOURCE ORA 00051 51
TOO_MANY_ROWS ORA 01422 1422
VALUE_ERROR ORA 06502 6502
ZERO_DIVIDE ORA 01476 1476

Definidas pelo programador com um nome
Diferente das exceptions pr-definidas, as exceptions definidas pelo programador devem
ser declaradas e devem ser chamadas explicitamente atravs da declarao:
RAISE
Definindo a Exception:
DECLARE
past_due EXCEPTION;
acct_num NUMBER(5);
Exemplo
DECLARE
past_due EXCEPTION;
acct_num NUMBER;
BEGIN
...
DECLARE incio do sub block
past_due EXCEPTION;
acct_num NUMBER;
BEGIN
...
IF ... THEN
RAISE past_due; esta exception no
executada
END IF;
...
END; fim do sub-block
EXCEPTION
WHEN past_due THEN no usada
...
END;

EXCEPTION_INIT pragma
Pragma uma instruo especial ao compilador. A EXCEPTION_INIT pragma indica que o
compilador deve associar um nome a uma exception que possui um nmero
correspondente.

Exemplo
ORA-2292 violated integrity constraining - child
record found
DECLARE
ainda_ha_empreg EXCEPTION;
PRAGMA EXCEPTION_INIT (ainda_ha_empreg, -2292);
BEGIN
DELETE FROM empresa;
EXCEPTION
WHEN ainda_ha_empreg THEN
DBMS_OUTPUT.PUT_LINE
( Ainda existem empregados para a empresa );
END;
Usando raise_application_error
A package DBMS_STANDARD, que vem com o Oracle7, tem algumas facilidades que
ajudam a aplicao a interagir com o Oracle. Por exemplo, a procedure
raise_application_error permite ao programador definir uma mensagem de
erro ao seu modo.

raise_application_error(error_number,
message[, {TRUE | FALSE}]);
Onde error_number um inteiro negativo entre -20000 .. -20999 e message uma string
de caracter com at 2048 bytes. Se o terceiro parmetro TRUE, o erro colocado em
uma pilha de erros. Se o parmetro FALSE (default), o erro substitui todos os erros
anteriores. A chamada a raise_application_error somente pode ser feita de uma stored
procedure. Quando chamada, o subprograma encerrado e o
nmero do erro e a mensagem so retornados.
CREATE PROCEDURE raise_salary (emp_id NUMBER, increase NUMBER)
AS
current_salary NUMBER;
BEGIN
SELECT sal INTO current_salary FROM emp
WHERE empno = emp_id;
IF current_salary IS NULL THEN
/* Issue user defined error message. */
raise_application_error( 20101, Salary is missing );
ELSE
UPDATE emp SET sal = current_salary + increase
WHERE empno = emp_id;
END IF;
END raise_salary;

Seo Exception
EXCEPTION
WHEN exception_name1 THEN handler
sequence_of_statements1
WHEN exception_name2 THEN another
handler
sequence_of_statements2
...
WHEN OTHERS THEN optional handler
sequence_of_statements3
END;

Usando SQLCODE e SQLERRM
No tratamento de uma exception pode-se usar as funes SQLCODE e SQLERRM. Para
exceptions internas, o nmero do SQLCODE negativo a menos que o erro seja
no_data_found que neste caso +100. O SQLERRM retorna a mensagem do Oracle.
Exemplo
DECLARE
err_num NUMBER;
err_msg VARCHAR2(100);
BEGIN
...
EXCEPTION
...
WHEN OTHERS THEN
err_num := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 100);
INSERT INTO errors VALUES (err_num, err_msg);
END;

Exerccio IV.1
Altere o script do exerccio I.1, incluindo a seo de exception (prevendo o caso em que o
nmero informado for menor que zero).

Exerccio IV.2
Construa um script que faa uma consulta simples base de dados (tabela scott.emp)
tomando como parmetro o nome do funcionrio e retorne a sua matrcula.
Utilize o comando SELECT INTO
Pense na possibilidade do funcionrio no ser encontrado
Pense na possibilidade de existirem dois funcionrios com o mesmo nome
Trate as demais exceptions de uma maneira genrica

Exerccio IV.3
Converta o script anterior (IV.2 retorno da matrcula do funcionrio dado um nome) para
uma procedimento (recebe o nome, retorna a matrcula e um status indicando se fez a
recuperao com sucesso, se no encontrou ou se existem registros duplicados).
Armazene este procedimento no banco (stored procedure).

Testes e Depurao

Diretrizes de Depurao
Encontrar o local onde ocorre o erro
Definir exatamente o problema
Reduzir o programa a um simples teste
Estabelecer um ambiente de teste

DBMS_OUTPUT
Pacote para mostrar mensagens:


Exemplo de DBMS_OUTPUT
BEGIN
DBMS_OUTPUT.ENABLE(20000);
DBMS_OUTPUT.PUT_LINE( Incio );
...
DBMS_OUTPUT.PUT_LINE( Fim );
END;
Procedure Builder
Ambiente de desenvolvimento de PL/SQL. Serve como depurador de cdigo PL/SQL. No
pode depurar stored procedures, somente chamar PL/SQL Interpreter Visualizador
Mostra o bloco, procedimento ou funo que est sendo executado
Linha de comandos
Permite execuo imediata de instrues de PL/SQL
Facilidades
Depurar sem necessidade de alterar o cdigo
Insero de breakpoints
Visualizao e alterao dos valores das variveis locais
Depurao mais simples
Ambiente de desenvolvimento integrado
PL/SQL Dinmico
DBMS_SQL
A instruo SQL pode ser montada dinamicamente dentro do bloco
Processa trs tipos de instrues:
instrues de DML e DDL
consultas blocos de PL/SQL annimos
Instrues SELECT no devem possuir a clusula INTO
No incluir ponto e vrgula no final, exceto blocos PL/SQL annimos
DBMS_SQL.OPEN_CURSOR. Abre o cursor para executar a instruo
SQL atribuindo um ID para o cursor que ser utilizado nas chamadas
subsequentes.
Sintaxe:
FUNCTION OPEN_CURSOR RETURN INTEGER;
DBMS_SQL.PARSE
Anlise da instruo
Verificao da sintaxe e semntica da instruo Se for uma consulta, determina o plano de
execuo
Sintaxe
PROCEDURE PARSE (cursor_id IN INTEGER,
instruo IN VARCHAR2,
indicador_linguagem IN
INTEGER);
onde indicador_linguagem pode ser V6, V7 ou NATIVE.
DBMS_SQL.BIND_VARIABLE
Associao de variveis de entrada de dados variveis reais do bloco PL/SQL
Sintaxe
PROCEDURE BIND_VARIABLE
(cursor_id IN INTEGER, :nome_na_instruo IN VARCHAR2, valor_para_atribuir IN
tipo_dado );
onde tipo_dado pode ser NUMBER, VARCHAR2 ou DATE.
H variaes para os demais tipos de dado DBMS_SQL.DEFINE_COLUMN.
Associao de variveis de sada de dados (resultado de uma consulta) variveis reais
do bloco PL/SQL.
Sintaxe
PROCEDURE DEFINE_COLUMN
(cursor_id IN INTEGER,
posio_coluna IN INTEGER,
nome_coluna IN tipo_dado);
Uma instruo para cada coluna da consulta
DBMS_SQL.EXECUTE
Para no consulta
executa a instruo e retorna o nmero de linhas processadas
Para consulta
determina o conjunto de linhas a serem processadas
Sintaxe
FUNCTION EXECUTE (cursor_id IN INTEGER)
RETURN INTEGER;
O retorno corresponde s linhas processadas somente se forem instrues INSERT,
UPDATE ou DELETE.
DBMS_SQL.FETCH_ROWS
Somente usado em consultas. Faz o fetch buscando os dados no servidor. Os dados de
retorno so convertidos nos tipos definidos com DEFINE_COLUMN.
Sintaxe
FUNCTION FETCH_ROWS (cursor_id IN INTEGER)
RETURN INTEGER;
O retorno o nmero de linhas.
DBMS_SQL.EXECUTE_AND_FETCH
Combina as operaes de EXECUTE e a primeira chamada de FETCH_ROWS.
Sintaxe
FUNCTION EXECUTE_AND_FETCH
(cursor_id IN INTEGER, busca_exata IN BOOLEAN DEFAULT FALSE)
RETURN INTEGER;
Se busca_exata for TRUE e a consulta retornar mais de uma linha, abre a exception
TOO_MANY_ROWS. O retorno o nmero de linhas.
DBMS_SQL.VARIABLE_VALUE
Determina o valor de uma varivel de associao, se for modificada pela instruo
(parmetros de sada). Utilizado quando a instruo um bloco PL/SQL.
Sintaxe
PROCEDURE VARIABLE_VALUE
(cursor_id IN NUMBER,
:nome_na_instruo IN VARCHAR2,
valor_variavel OUT tipo_dado);
DBMS_SQL.COLUMN_VALUE
Somente usado em consultas, aps FETCH_ROWS. Devolve realmente os dados.
Variveis devem ser do mesmo tipo definido em DEFINE_COLUMN.
Sintaxe
PROCEDURE COLUMN_VALUE
(cursor_id IN INTEGER,
posio_coluna IN INTEGER,
valor_coluna OUT tipo_dado);
DBMS_SQL.CLOSE_CURSOR
Fecha o cursor. Libera os recursos utilizados pelo cursor.
Sintaxe
PROCEDURE CLOSE_CURSOR
(cursor_id IN OUT INTEGER)

Executar instrues DML (exceto consultas). Passos necessrios:
Abrir o cursor (OPEN_CURSOR)
Analisar a instruo (PARSE)
Associar quaisquer variveis de entrada de
dados (BIND_VARIABLE)
Executar a instruo (EXECUTE)
Fechar o cursor (CLOSE_CURSOR)

Executar instrues DDL. Passos necessrios:
Abrir o cursor (OPEN_CURSOR)
Analisar a instruo (PARSE)
Fechar o cursor (CLOSE_CURSOR)
No pode utilizar bind variables em DDL
As instrues so executadas j no PARSE.

EXECUTE. Executar consultas. Passos necessrios:
Abrir o cursor (OPEN_CURSOR)
Analisar a instruo (PARSE)
Associar quaisquer variveis de entrada de dados (BIND_VARIABLE)
Definir variveis de sada de dados (DEFINE_COLUMN)
Executar a consulta (EXECUTE)
Extrair as linhas (FETCH_ROWS)
Devolver os resultados s variveis PL/SQL (COLUMN_VALUE)
Fechar o cursor (CLOSE_CURSOR)

Executar PL/SQL. Passos necessrios:
Abrir o cursor (OPEN_CURSOR)
Analisar a instruo (PARSE)
Associar quaisquer variveis de entrada de dados (BIND_VARIABLE)
Executar a instruo (EXECUTE)
Obter o valor de quaisquer variveis de sada de dados (VARIABLE_VALUE)
Fechar o cursor (CLOSE_CURSOR)

Novas facilidades PL/SQL 8.0
Capacidade de analisar cadeias de caracteres de SQL de grandes dimenses
Processamento matricial
Associar e definir tipos Oracle8 (objetos, LOBs)
Procedimento DESCRIBE_COLUMNS

Sugestes e Tcnicas
Reutilizar cursores
Um cursor aberto pode processar instrues SQL diferentes. Instrues repetidas no
necessitam repetir o PARSE, apenas o EXECUTE.
Permisses
Os perfis de grupo so desativados em procedimentos empacotados, incluindo o
DBMS_SQL
Operaes de DDL e lock
Entrada e Sada em Arquivos PL/SQL I /O em Arquivos OS. A release 7.3 do Oracle Server
adiciona a capacidade de se fazer I/O atravs da package UTL_FILE. Isto similar quelas
operaes padres em arquivos de sistemas operacionais (OPEN, GET, PUT, CLOSE)
com algumas limitaes. Por exemplo, pode-se chamar a funo FOPEN para retornar um
arquivo aberto, no qual as chamadas subsequentes aos comandos GET_LINE ou PUT so
executados nos arquivos. Enquanto no se fechar o arquivo (FCLOSE) as operaes de
I/O no estaro completas.

Procedures da Package UTL_FILE

Function/Procedure Description
FOPEN Abre um arquivo para entrada e sada. Cria um arquivo de sada se ele no existir
IS_OPEN Determina se um arquivo est aberto
FCLOSE Fecha um arquivo
FCLOSE_ALL Fecha todos os arquivos abertos
GET_LINE L uma linha texto de um arquivo aberto.
PUT Escreve uma linha no arquivo. No adiciona um caracter de terminao (EOL)
PUT_LINE Escreve uma linha no arquivo. Adiciona um caracter de terminao
PUTF Procedure com formatao.
NEW_LINE Escreve uma ou mais linhas de terminao no arquivo.
FFLUSH Escreve fisicamente todas as linhas pendentes para o arquivo de sada.

Segurana
O diretrio de acesso dos arquivos deve ser especificado em um parmetro de inicializao
no arquivo INIT.ORA
UTL_FILE_DIR = < directory name>
A especificao do parmetro:
UTL_FILE_DIR = *
Torna sem efeito a segurana.

Declarao de Tipos
A especificao para a package UTL_FILE declarada no tipo:
TYPE file_type IS RECORD (id
BYNARY_INTEGER)
Exemplo:
v_filehandle UTL_FILE.FILE_TYPE;
...
v_filehandle := UTL_FILE.FOPEN(...);
...

Exceptions
Exception Name Descrio
INVALID_PATH Localizao ou nome do arquivo invlido
INVALID_MODE Modo de abertura do arquivo invlido.
INVALID_FILEHANDLE Arquivo Invlido.
INVALID_OPERATION Arquivo no pode ser aberto.
READ_ERROR Um erro de sistema operacional ocorreu durante leitura.
WRITE_ERROR Um erro de sistema operacional ocorreu durante a escrita..
INTERNAL_ERROR Um erro no especificado ocorreu no PL/SQL.
FOPEN
Sintaxe:
FUNCTION FOPEN(location IN VARCHAR2,
filename IN VARCHAR2,
open_mode IN
VARCHAR2)
RETURN UTL_FILE.FILE_TYPE;

Parmetros Descrio
location Diretrio
filename Nome do Arquivo
open_mode r ler um texto (GET_LINE)
w escrever um texto
(PUT, PUT_LINE, NEW_LINE, PUTF, FFLUSH)
a adicionar um texto
(PUT, PUT_LINE, NEW_LINE, PUTF, FFLUSH)
Retorno da Funo FOPEN
FOPEN retorna um file handle que deve ser passado para todas as procedures
chamadas posteriormente.

IS_OPEN
Sintaxe:
FUNCTION IS_OPEN(file_handle IN
FILE_TYPE)
RETURN BOOLEAN;
Parmetro Descrio
file_handle Um file handle ativo retornado na funo FOPEN

FCLOSE
Sintaxe:
PROCEDURE FCLOSE (file_handle IN OUT
FILE_TYPE);
Parmetro Descrio
file_handle Um file handle ativo retornado na funo FOPEN

FCLOSE_ALL
Sintaxe:
PROCEDURE FCLOSE_ALL;

GET_LINE
Sintaxe:
PROCEDURE GET_LINE
(file_handle IN FILE_TYPE,
buffer OUT VARCHAR2);
Parmetro Descrio
file_handle Um file handle ativo retornado na funo FOPEN
buffer Um buffer para receber a linha lida do arquivo.

PUT
Sintaxe:
PROCEDURE PUT
(file_handle IN FILE_TYPE,
buffer IN VARCHAR2);
Parmetro Descrio
file_handle Um file handle ativo retornado na funo FOPEN
buffer Um buffer que contm a linha a ser escrita.

NEW_LINE
Sintaxe:
PROCEDURE NEW_LINE
(file_handle IN FILE_TYPE,
lines IN NATURAL := 1);
Parametro Descrio
file_handle Um file handle ativo retornado na funo FOPEN
lines Nmero de linhas de terminao a serem
escritas no arquivo.

PUT_LINE
Sintaxe:
PROCEDURE PUT_LINE
(file_handle IN FILE_TYPE,
buffer IN VARCHAR2);
Parmetro Descrio
file_handle Um file handle ativo retornado na funo FOPEN
buffer Um buffer que contem a linha a ser escrita.

FFLUSH
Sintaxe:
PROCEDURE FFLUSH
(file_handle IN FILE_TYPE);
Parmetro Descrio
file_handle Um file handle ativo retornado na funo FOPEN
Exemplo
PROCEDURE file_to_table
(loc_in IN VARCHAR2, file_in IN VARCHAR2,
table_in IN names_tabtype)
IS
names_file CONSTANT UTL_FILE.FILE_TYPE :=
UTL_FILE.FOPEN (loc_in, file_in, R);
line_counter INTEGER := 1;
BEGIN
LOOP
UTL_FILE.GET_LINE(names_file,
table_in(line_counter));
line_counter := line_counter + 1;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
UTL_FILE.FCLOSE(names_file);
END;

Desempenho e Otimizao
Instncia Oracle
SGA: Sistem Global Area
Shared Pool
Armazena instrues SQL recebidas do BD
Triggers
Manter o cdigo o mais pequeno possvel, atravs de chamadas de subprogramas
armazenados antes da verso 7.3 os triggers no eram armazenados de forma compilada

Otimizao de instrues SQL
Explain Plan
Tabela plan_ table
Pode ser criada localmente atravs de $ORACLE_HOME\ rdbms\admin\utlxplan.sql
No SQL* Plus
SET AUTOTRACE ON
Aps cada instruo faz automaticamente o explain plan

Tkprof
ALTER SESSION SET SQL_TRACE=TRUE
gera um arquivo de log de todas as instrues SQL, no formato ora_nnnnn.trc
Aps todos os comandos efetuados, fechar o arquivo de log, alterando para FALSE
Executar tkprof para formatar o arquivo .trc

Anlise de Resultados
NESTED LOOP
Operao necessria para executar os joins de tabelas
TABLE ACCESS (FULL)
Pesquisa integral, buscando todas as linhas da tabela
TABLE ACCESS (BY ROWID)
Modo mais rpido de buscar uma s linha
INDEX: {UNIQUE | RANGE} SCAN

Tcnicas
Verificar questes de rede. Utilizar PL/SQL no Client sempre que possvel. Evitar
repeties de parse desnecessrias.
Interface matricial Oracle
permite grandes quantidades de dados
enviados pela rede como uma unidade

Vous aimerez peut-être aussi