Você está na página 1de 154

Treinamento

Sumrio
01 ntroduo ao PL/SQL.............................................................................................................................. 5
Objetivo do Curso........................................................................................................................................ 6
Conceitos Bsicos....................................................................................................................................... 7
O que PL/SQL?................................................................................................................................... 7
Por que aprender a PL/SQL?............................................................................................................ 7
Blocos PL/SQL....................................................................................................................................... 7
Comentrios........................................................................................................................................... 8
Execuo de Blocos............................................................................................................................... 9
Ambiente de Execuo.............................................................................................................................. 10
Ferramentas para execuo de declaraes SQL e desenvolvimento PL/SQL...................................10
SQL*Plus......................................................................................................................................... 10
PL/SQL Developer ......................................................................................................................... 12
TOAD............................................................................................................................................... 15
Oracle SQL Developer..................................................................................................................... 18
Declarao de variveis............................................................................................................................ 23
Explorando os datatypes...................................................................................................................... 23
Datatypes escalares (tipos de dados simples)................................................................................ 23
Tipo Registro................................................................................................................................... 26
O Escopo de uma varivel.................................................................................................................... 28
Usando tags para identificar um varivel......................................................................................... 29
Valores Nulos....................................................................................................................................... 29
Tratamento de excees........................................................................................................................... 31
Funcionamento geral............................................................................................................................ 31
Capturando uma exceo................................................................................................................ 32
Excees pr-definidas da Oracle........................................................................................................ 32
Erros indefinidos da Oracle.................................................................................................................. 33
Erros definidos pelo usurio................................................................................................................. 34
SQLCODE e SQLERRM...................................................................................................................... 35
O procedimento raise_application_error.......................................................................................... 36
Regras de escopo de exceo............................................................................................................. 37
Propagando as excees..................................................................................................................... 37
Exerccios propostos................................................................................................................................. 38
02 Estruturas PL/SQL.................................................................................................................................. 39
Cenrio para prtica.................................................................................................................................. 40
Modelo lgico de entidade e relacionamento....................................................................................... 40
Modelo fsico de entidade e relacionamento........................................................................................ 41
Declaraes F e LOOPs........................................................................................................................... 43
A declarao F..................................................................................................................................... 43
A declarao F...THEN...ELSE....................................................................................................... 43
A declarao F...ELSF................................................................................................................... 44
Loop Simples........................................................................................................................................ 45
Criando um loop REPEAT...UNTL.................................................................................................. 45
Loop FOR............................................................................................................................................. 45
Loop WHLE......................................................................................................................................... 46
Qual loop devo usar?............................................................................................................................ 47
Utilizao de Cursores............................................................................................................................... 48
Conceitos bsicos................................................................................................................................. 48
Cursores Explcitos............................................................................................................................... 48
Declarando um cursor..................................................................................................................... 48
Abrindo um Cursor........................................................................................................................... 49
Recuperando dados de um Cursor.................................................................................................. 50
Fechando o Cursor.......................................................................................................................... 51
Elaborado por Josinei Barbosa da Silva
Pgina 1 de 154
Treinamento
Atributos de cursores explcitos....................................................................................................... 52
Cursores, registros e o atributo %ROWTYPE................................................................................. 53
Cursores explcitos automatizados (LOOP Cursor FOR)................................................................55
Passando parmetros para cursores............................................................................................... 57
Cursores implcitos............................................................................................................................... 57
Atributos do cursor implcito............................................................................................................ 58
Variveis de cursor............................................................................................................................... 59
Blocos annimos, procedimentos e funes.............................................................................................. 61
Exerccios propostos................................................................................................................................. 64
03 Stored Procedures.................................................................................................................................. 65
Procedimentos armazenados (Stored Procedure)..................................................................................... 66
Por que usar os procedimentos?.......................................................................................................... 66
Procedimentos versus funes............................................................................................................. 66
Procedimentos................................................................................................................................. 67
Funes........................................................................................................................................... 68
Manuteno de procedimentos armazenados...................................................................................... 70
Usando os parmetros......................................................................................................................... 70
Definies de parmetro.................................................................................................................. 71
Dependncias de procedimentos......................................................................................................... 71
Segurana da invocao de procedimento........................................................................................... 71
Pacotes...................................................................................................................................................... 72
Vantagens do uso de pacotes......................................................................................................... 72
Estrutura de um pacote........................................................................................................................ 72
A especificao do pacote............................................................................................................... 72
O corpo do pacote........................................................................................................................... 73
Utilizando os subprogramas e variveis de um pacote......................................................................... 75
Manuteno dos pacotes...................................................................................................................... 76
Estados de um pacote..................................................................................................................... 76
Recompilando pacotes.................................................................................................................... 76
Triggers..................................................................................................................................................... 77
O que um Trigger?............................................................................................................................. 77
Triggers DML........................................................................................................................................ 77
Tipos de triggers DML..................................................................................................................... 78
Ativando e desativando triggers............................................................................................................ 79
Exerccios Propostos................................................................................................................................. 80
04 Collections.............................................................................................................................................. 82
Colees.................................................................................................................................................... 83
Tabelas por ndice................................................................................................................................ 83
Declarando uma tabela por ndice................................................................................................... 83
Manipulando uma tabela por ndice ................................................................................................ 84
Tabelas aninhadas............................................................................................................................... 87
Declarando uma tabela aninhada.................................................................................................... 87
Manipulando tabelas aninhadas...................................................................................................... 88
Arrays de tamanho varivel.................................................................................................................. 91
Declarando e inicializando um VARRAY......................................................................................... 92
Adicionando e removendo dados de um VARRAY.......................................................................... 92
Mtodos de tabela da PL/SQL.............................................................................................................. 94
Executando declaraes SELECT em uma coleo............................................................................ 95
Criando um cursor explcito a partir de uma coleo.......................................................................98
O bulk binding....................................................................................................................................... 99
Usando BULK COLLECT.............................................................................................................. 100
Usando FORALL........................................................................................................................... 102
Tratamento de excees para as colees........................................................................................ 103
Exerccios Propostos.......................................................................................................................... 105
05 Tpicos avanados: PL/SQL................................................................................................................ 106
Elaborado por Josinei Barbosa da Silva
Pgina 2 de 154
Treinamento
SQL Dinmico.......................................................................................................................................... 107
SQL Dinmico em cursores................................................................................................................ 110
Stored Procedure com transao autnoma........................................................................................... 110
Tabela Funo PL/SQL (PPELNED)..................................................................................................... 114
Montando uma funo PPELNED.................................................................................................... 115
Utilizando uma funo PPELNED em uma declarao SQL............................................................117
UTL_FLE: Escrita e leitura de arquivos no servidor................................................................................ 118
Procedimentos e funes de UTL_FLE............................................................................................. 119
Gerando um arquivo com informaes extradas do banco de dados................................................121
Recuperando informaes de um arquivo.......................................................................................... 122
TEXT_O............................................................................................................................................. 122
Exerccios Propostos............................................................................................................................... 124
06 Tpicos avanados: SQL e funes incorporadas do Oracle Database...............................................125
SQLs avanados para usar com PL/SQL................................................................................................ 126
O outer join do Oracle......................................................................................................................... 126
ROWNUM........................................................................................................................................... 127
Comando CASE no SELECT............................................................................................................. 128
SELECT combinado com CREATE TABLE e NSERT.......................................................................128
CREATE TABLE AS SELECT....................................................................................................... 129
NSERT . SELECT...................................................................................................................... 129
MERGE.............................................................................................................................................. 129
GROUP BY com HAVNG.................................................................................................................. 131
Recursos avanados de agrupamento: ROLLUP e CUBE.................................................................132
ROLLUP........................................................................................................................................ 132
CUBE............................................................................................................................................. 132
Consultas hierrquicas com CONNECT BY....................................................................................... 133
Funes incorporadas do Oracle Database............................................................................................. 134
07 Dicas de performance e boas prticas em SQL................................................................................... 137
ntroduo................................................................................................................................................ 138
O Otimizador Oracle................................................................................................................................ 138
Otimizador baseado em regra (RBO)................................................................................................. 138
Otimizador baseado em custo (CBO)................................................................................................. 139
Variveis de ligao (Bind Variables)...................................................................................................... 139
SQL Dinmico.......................................................................................................................................... 141
O uso de ndices...................................................................................................................................... 142
Colunas indexadas no ORDER BY..................................................................................................... 143
EXPLAN PLAN...................................................................................................................................... 143
O AUTOTRACE do SQL*Plus............................................................................................................ 144
A clusula WHERE crucial!................................................................................................................... 147
Use o WHERE ao invs do HAVNG para filtrar linhas.......................................................................148
Especifique as colunas principais do ndice na clusula WHERE......................................................148
Evite a clausula OR............................................................................................................................ 148
Cuidado com "Produto Cartesiano.................................................................................................... 148
SQLs complexas...................................................................................................................................... 149
Quando usar MNUS, N e EXSTS.................................................................................................... 149
Evite o SORT........................................................................................................................................... 150
EXSTS ao invs de DSTNCT.......................................................................................................... 150
UNON e UNON ALL.............................................................................................................................. 150
Cuidados ao utilizar VEWs..................................................................................................................... 151
Joins em VEWs complexas............................................................................................................... 151
Reciclagem de VEWs........................................................................................................................ 151
Database Link.......................................................................................................................................... 151
Aplicativos versus Banco de Dados......................................................................................................... 151
Utilize o comando CASE para combinar mltiplas varreduras:..........................................................151
Utilize blocos PL/SQL para executar operaes SQL repetidas em LOOPs de sua aplicao..........152
Elaborado por Josinei Barbosa da Silva
Pgina 3 de 154
Treinamento
Use a clausula RETURNNG em declaraes DML:.......................................................................... 153
Exerccios Propostos............................................................................................................................... 154
Elaborado por Josinei Barbosa da Silva
Pgina 4 de 154
Treinamento
01 Introduo ao PL/SQL
1. Objetivo do curso;
2. Conceitos bsicos;
3. Declarao de variveis;
4. Tratamento de excees;
Elaborado por Josinei Barbosa da Silva
Pgina 5 de 154
Treinamento
Objetivo do Curso
O objetivo do curso apresentar aos desenvolvedores, recursos da linguagem PL/SQL que
podero auxili-los na manipulao de dados armazenados no banco de dados Oracle.
Grande parte dos recursos apresentados, como cursores e colees, sero explorados dentro de
unidades de programas, que na PL/SQL so representadas por:
Blocos annimos;
Stored procedures;
e Packages;
Conceitos bsicos como declarao de variveis, tipos e tratamento de excees, tambm sero
mostrados e ao final do curso, ser apresentado um tpico de otimizao e boas prticas de SQL.
Elaborado por Josinei Barbosa da Silva
Pgina 6 de 154
Treinamento
Conceitos Bsicos
O que !"S#!$
PL/SQL (Procedural Language/Structured Query Language) uma extenso de linguagem de
procedimentos desenvolvida pela Oracle para a SQL Padro, para fornecer um modo de executar a lgica
de procedimentos no banco de dados.
A SQL em si uma linguagem declarativa poderosa. Ela declarativa pois voc descreve resultados
desejados, mas no o modo como eles so obtidos. sso bom porque voc pode isolar um aplicativo dos
detalhes especficos de como os dados so armazenados fisicamente. Um programador competente em
SQL tambm consegue eliminar uma quantidade grande de trabalho de processamento no nvel do servidor
por meio do uso criativo da SQL.
Entretanto, existem limites para para aquilo que voc pode realizar com uma nica consulta
declarativa. O mundo real geralmente no to simples como desejaramos que ele fosse. Os
desenvolvedores quase sempre precisam executar vrias consultas sucessivas e processar os resultados
especficos de uma consulta antes de passar para a consulta seguinte. sso cria dois problemas para um
ambiente cliente/servidor:
1. A lgica de procedimentos, ou seja, a definio do processo, reside nas mquinas do cliente;
2. A necessidade de olhar os dados de uma consulta e us-los como a base da consulta
seguinte resulta em uma quantidade maior de trfego de rede.
A PL/SQL fornece um mecanismo para os desenvolvedores adicionarem um componente de
procedimento no nvel de servidor. Ela foi aperfeioada a ponto de os desenvolvedores agora terem acesso
a todos os recursos de uma linguagem de procedimentos completa no nvel de servidor. Ela tambm forma
a base da programao do conjunto da Oracle de ferramentas de desenvolvimento para cliente/servidor,
como Forms & Reports.
or que a%render a !"S#!$
ndependente da ferramenta front-end que est usando, voc pode usar a PL/SQL para executar o
processamento no servidor em vez de execut-lo no cliente. Voc pode usar a PL/SQL para encapsular
regras de negcios e outras lgicas complicadas. Ela fornece modularidade e abstrao. Voc pode us-la
nos gatilhos (triggers) do banco de dados para codificar restries complexas, as quais reforam a
integridade do banco de dados, para registrar alteraes e para replicar os dados.
A PL/SQL tambm pode ser usada com os procedimentos armazenados e as funes para fornecer
maior segurana ao banco de dados. Finalmente,ela fornece um nvel de independncia da plataforma. O
Oracle est disponvel para muitas plataformas de hardware, mas a PL/SQL igual em todas elas.
Blocos !"S#!
A PL/SQL chamada de linguagem estruturada em blocos. Um bloco PL/SQL uma unidade
sinttica que pode conter cdigo de programa, declaraes de variveis, handlers de erro, procedimento,
funes e at mesmo outros blocos PL/SQL.
Cada unidade de programa do PL/SQL consiste em um ou mais blocos. Cada bloco pode ser
completamente separado ou aninhado com outros blocos.
Um bloco PL/SQL formado por trs sesses:
1. Declarativa (opcional);
2. Executvel;
3. Excees (tambm opcional).
Elaborado por Josinei Barbosa da Silva
Pgina 7 de 154
Treinamento
A rea declarativa, indicada pela palavra chave DECLARE, a parte inicial do bloco e reservada para
declarao de variveis, constantes, tipos, excees definidas por usurios, cursores e subrotinas.
A seo executvel contm os comandos SQL e PL/SQL que iro manipular dados do banco e
iniciada pela palavra chave BEGIN. Quando o bloco no possui a sesso de excees, a seo executvel
finalizada pela palavra chave END, seguida do ponto e vrgula, do contrrio, finalizada pelo incio da
sesso de excees, que indicada pela palavra chave EXCEPTION.
A sesso executvel, iniciada pelo BEGIN e finalizada pelo END seguido do ponto e vrgula, a nica
sesso obrigatria em um bloco PL/SQL.
O Bloco annimo o tipo mais simples de um bloco PL/SQL e possui a estrutura mostrada na figura
abaixo:
Figura 01: Estrutura de bloco annimo do PL/SQL.
No decorrer do curso, veremos um outro tipo de bloco PL/SQL: Stored Procedure.
Comentrios
Os comentrios so utilizados para documentar cada fase do cdigo e ajudar no debug..
Os comentrios so informativos e no alteram a lgica ou o comportamento do programa. Quando
usados de maneira eficiente, os comentrios melhoram a leitura e as futuras manutenes do programa.
Em PL/SQL, os comentrios podem ser indicados de duas formas:
Usando os caracteres -- no incio da linha a ser comentada;
ou concentrando o texto (ou cdigo) entre os caracteres '/*' e '*/'.
Abaixo temos o exemplo de um bloco PL/SQL que no faz nada, mas demonstra o uso de
comentrios:
DECLARE
-- Declarao de variveis
Elaborado por Josinei Barbosa da Silva
Pgina 8 de 154
Treinamento
BEGIN
/*O programa no executa nada*/
NULL;
EXCEPTION --Aqui inicia a sesso e excees
/*A rea de exceo tambm no faz nada*/
NULL;
END;
Utilizar '--' em comentrios de uma linha e '/* */' em comentrios de mais de uma linha, uma boa
%rtica de programao.
&xecuo de Blocos
Na execuo de um bloco PL/SQL:
Colocar um ponto e vrgula(;) no final de cada comando SQL ou PL/SQL;
Quando um bloco executado com sucesso, sem erros de compilao ou erros no tratados,
a seguinte mensagem ser mostrada:
PL_SQL procedure successfully completed
No colocar ponto e vrgula aps as palavras chaves DECLARE, BEGIN e EXCEPTION;
END e qualquer outro comando PL/SQL e SQL so terminados por ponto e vrgula;
Comandos podem ser colocados na mesma linha, mas no recomendado, pois dificulta a
visualizao do cdigo;
Abaixo segue o exemplo de um bloco PL/SQL, para ser executado no SQL*Plus, que imprime na tela
o total de produtos cadastrados no banco de dados:
DECLARE
-- Declarao de variveis
vCount INTEGER;
BEGIN
-- Recuperar quantidade de produtos cadastrados
SELECT count(*)
INTO vCount
FROM produto;

-- Imprimir, na tela, a quantidade de produtos cadastrados
dbms_output.put_line('Exist! '##to_char(vCount)##' c$%$st&$%os'');
EXCEPTION
/* Se ocorrer qualquer erro, informar o usurio que no foi possvel
verificar a quantidade de produtos cadastrados */
()EN OT)ERS T)EN
dbms_output.put_line('N*o +oi ,oss-./ .&i+ic$& $ 0u$nti%$% %'##
',&o%utos c$%$st&$%os'');
END;
1
Elaborado por Josinei Barbosa da Silva
Pgina 9 de 154
Treinamento
'mbiente de &xecuo
O ambiente de execuo PL/SQL possui dois componentes essenciais:
Motor PL/SQL;
lient do Oracle (com uma Ferramenta de desenvolvimento).
O Motor PL/SQL parte do Oracle Database e, portanto, ao instalar o SGBD da Oracle, o motor
PL/SQL estar pronto para ser utilizado.
A ferramenta de desenvolvimento pode ser qualquer uma que permita o desenvolvimento de cdigo
PL/SQL e execuo de declaraes SQL.
Quando voc executa um declarao SQL em uma ferramenta, os seguintes passos devem
acontecer:
1. A ferramenta transmitir a declarao SQL pela rede para o servidor de banco de dados;
2. A ferramenta aguardar uma resposta do servidor de banco de dados;
3. O servidor de banco de dados executar a consulta e transmitir os resultados de volta para a
ferramenta;
4. A ferramenta exibir os resultados da declarao.
Ns j vimos como montar um bloco PL/SQL e executamos esse bloco no SQL*PLUS. Pois, bem, o
SQL*PLUS uma ferramenta para execuo de declaraes SQL e desenvolvimento PL/SQL.
Existem muitas outras ferramentas para desenvolvimento e execuo de blocos PL/SQL e, embora
no seja o foco deste treinamento, veremos a seguir algumas destas ferramentas.
(erramentas %ara execuo de declaraes S#! e desenvolvimento !"S#!
S#!)lus
O SQL*Plus uma ferramenta nativa da Oracle que acompanha todas as verses do Oracle
Database e est disponvel na verso grfica (Windows) e/ou na verso de prompt. Em ambas verses, os
recursos so os mesmos.
Abaixo voc pode observar, atravs das figuras, os dois ambientes:
Elaborado por Josinei Barbosa da Silva
Pgina 10 de 154
Treinamento
Figura 02: Ambiente grico do SQL!Plus
Figura 0": Ambiente de #rom#t do $S%&'S
Embora seja uma ferramenta poderosa, o SQL*Plus no popular entre os desenvolvedores PL/SQL,
pois no disponibiliza recursos com os quais eles esto acostumados, como por exemplo, debugger, auto-
complete, exibio de resultados em grid e botes grficos para manipulao e execuo dos comandos.
A maior parte dos usurios do SQL*Plus, se concentra na comunidade de DBAs que utilizam a
ferramenta para manuteno do banco de dados, pois atravs do SQL*Plus que os scripts de manuteno
so executados.
Mas mesmo sendo um desenvolvedor, importante saber que sempre que existir na mquina uma
instalao do Server ou do lient Oracle, o SQL*Plus estar disponvel (ele pode ser muito til se voc
estiver atendendo um cliente que no possui ferramentas de desenvolvimento!).
Elaborado por Josinei Barbosa da Silva
Pgina 11 de 154
Treinamento
Para conhecer detalhes de uso e configurao do ambiente do SQL*Plus, consulte o site da Oracle
(http://www.oracle.com/pls/db92/db92.sql_keywords?letter=A&category=sqlplus).
!"S#! Develo%er
Provavelmente este o ambiente de desenvolvimento PL/SQL mais utilizado pelos desenvolvedores
e perfeitamente justificvel, pois trata-e de um ambiente completo de desenvolvimento e muito simples de
usar.
PL/SQL Developer um ambiente de desenvolvimento integrado (DE) que foi especialmente
destinado ao desenvolvimento de programas armazenados em bancos de dados Oracle, ou seja, vai muito
alm do desenvolvimento de blocos PL/SQL e execuo de comandos SQL.
Entre os vrios recursos para desenvolvedores, podemos destacar:
Debu**er inte*rado+ Um debugger poderoso, muito prximo dos encontrados em ambientes
RAD, como o Delphi. Nele voc encontrar recursos como Breakpoints, Watches e muito mais.:
Figura 0(: &ebuger do PL/SQL &e)elo#er
#uer, Builder+ Uma tarefa que muito desenvolvedor detesta relacionar tabelas,
principalmente em bancos de dados onde comum o uso de chaves primrias compostas. Com o
Query Builder muito fcil realizar esse trabalho. Se as chaves primrias e estrangeiras estiverem
todas definidas, o usurio montar suas querys apenas utilizando o mouse:Figura 05: Query Builder
do PL/SQL Developer
Elaborado por Josinei Barbosa da Silva
Pgina 12 de 154
Treinamento
S#! -indo.+ Ferramenta ideal para execuo de declaraes de recuperao (SELECT),
alterao (UPDATE), incluso (NSERT) ou excluso (DELETE). Os resultados dos comandos de
recuperao so exibidos em grid:
Figura 0*: SQL +indo, do PL/SQL &e)elo#er
Command -indo.+ deal para execuo de blocos annimos , scripts e comandos de
alterao de objetos (DDL). Seu funcionamento eqivale ao do SQL*PLUS, com a grande vantagem
Elaborado por Josinei Barbosa da Silva
Pgina 13 de 154
Treinamento
de contar com um editor integrado e recursos como o auto-complete e teclas de atalho para
execuo do script contido em seu editor. Muitos comandos do SQL*PLUS como o SE!
SE"#E"$%!P%! $& e o L S", tambm funcionam nessa ferramenta:
Figura 0-: Editor do .ommand +indo, do PL/SQL &e)elo#er
Figura 0/: 0anela de e1ecu23o do .ommand +indo, do PL/SQL &e)elo#er
Object Bro.ser+ Lista de forma hierrquica os objetos permitidos ao usurio logado,
Elaborado por Josinei Barbosa da Silva
Pgina 14 de 154
Treinamento
disponibilizando atalhos que facilitam a manuteno e edio desses objetos:
Figura 04: 'b5ect 6ro,ser do PL/SQL &e)elo#er
O PL/SQL Developer no uma ferramenta gratuita, mas pode ser baixado para teste atravs do site
de seu fabricante: www.allroundautomations.com
TO'D
Outra ferramenta de grande aderncia ao trabalho dos desenvolvedores. Muito semelhante ao
PL/SQL Developer e amplamente conhecida entre os profissionais de PL/SQL.
Possui todos recursos encontrados em seu concorrente, mudando em alguns casos, a maneira de
usar.
Na figura a seguir, podemos observar que o object browser do TOAD segmentado por tipo de
objeto, mas segue a mesma idia do PL/SQL Developer.
O seu editor tambm avanado e em suas opes de menu encontramos recursos como SQL
Builder (equivalente ao Query Builder do PL/SQL Developer) , visualizadores de sesses e ferramentas de
tunning.
Elaborado por Josinei Barbosa da Silva
Pgina 15 de 154
Treinamento
Figura 10: Edi23o de stored #rocedure no 7'A&
Elaborado por Josinei Barbosa da Silva
Pgina 16 de 154
Treinamento
Figura 11: ' SQL 6uilder do 7'A&
Elaborado por Josinei Barbosa da Silva
Pgina 17 de 154
Treinamento
Figura 12: ' SQL Edit +indo, do 7'A&
A ferramenta TOAD possui verses gratuitas e pagas, podendo ser facilmente encontrada para
download na internet.
Para maiores informaes, visite o site da Quest, fabricante da ferramenta: www.quest.com
Oracle S#! Develo%er
Embora o SQL*Plus seja a ferramenta nativa do Banco de dados Oracle para execuo de comandos
SQL e PL/SQL, a Oracle disponibiliza para download gratuito em seu site, uma outra ferramenta para
desenvolvedores PL/SQL: O Oracle SQL Developer.
Essa ferramenta, desenvolvida em Java, disponibiliza para o desenvolvedor todos os recursos
necessrios para escrever seus programas PL/SQL.
Alm dos recursos j vistos no PL/SQL Developer e no TOAD, como debugger, wizards para
montagem de querys e object browser, o Oracle SQL Developer possui alguns recursos de destaque,
como:
Trabal/ar com conexes simult0neas+ No Oracle SQL Developer voc consegue ativar
mais de uma conexo por vez e carregar janelas em forma de abas, para cada conexo, como
mostrado na figura abaixo:
Elaborado por Josinei Barbosa da Silva
Pgina 18 de 154
Treinamento
Figura 1": 8rias cone19es abertas no 'racle SQL &e)elo#er: )isuali;adas na tela #rinci#al
Elaborado por Josinei Barbosa da Silva
Pgina 19 de 154
Treinamento
Figura 1(: Alterando a cone13o da aba de trabal<o ati)a no 'racle SQL &e)elo#er
Diversas 1ormas de resultados na mesma tela2 atravs de abas+
Elaborado por Josinei Barbosa da Silva
Pgina 20 de 154
Treinamento
Figura 1=: >esultado da instru23o: a#resentados de )rias maneiras: atra)?s de abas
&ditor com o%o de ocultar blocos+ No Oracle SQL Developer voc pode ocultar um bloco
e para visualiz-lo novamente basta um clique para expandir sua rea novamente Caso queira
visualiz-lo sem expandir, basta posicionar o cursor do mouse sobre o boto de expanso, como a
seguir:
Elaborado por Josinei Barbosa da Silva
Pgina 21 de 154
Treinamento
Figura 1*: Editor @ue oculta blocos e e1ibe a#enas #ara leitura com um sim#les mo)imento do mouse
O Oracle SQL Developer uma ferramenta "pesada", provavelmente por ter sido desenvolvida na
plataforma Java, por isso, necessrio uma mquina com bons recursos de memria e processamento
para seu uso, do contrrio, o desenvolvedor pode sofrer com a navegao da ferramenta.
A escolha da ferramenta depende do gosto do desenvolvedor. Esse treinamento pode ser praticado
em qualquer uma das ferramentas apresentadas aqui ou outras que sigam os padres para execuo de
SQL e PL/SQL.
Elaborado por Josinei Barbosa da Silva
Pgina 22 de 154
Treinamento
Declarao de variveis
Devemos sempre declarar as variveis na Sesso Declarativa antes de referenci-las nos blocos
PL/SQL. Podemos atribuir um valor inicial para a varivel criada, definir um valor que ser constante e
defini-la como NOT NULL.
Sintaxe:
Identificador [CONSTANT] Tipo de Dado [NOT NULL] [ 23 | DEFAULT expresso]
onde temos:
3denti1icador+ nome da varivel.
CO4ST'4T+ Contm valores que no podem ser alterados. Constantes devem ser
inicializadas.
Ti%o de Dado+ um tipo de dado que pode ser escalar, composto, referencial ou LOB.
4OT 45!!+ valida a varivel que sempre deve conter valor. Variveis NOT NULL tambm
devem ser inicializadas.
&x%resso+ qualquer expresso PL/SQL. Pode ser uma expresso literal, outra varivel ou
uma expresso envolvendo operadores e funes.
Ao declarar variveis, procure:
a) Adotar padres para dar nomes a variveis. Por exemplo, vNome para representar uma
varivel e cNome para representar uma varivel de coleo;
b) Se usar uma varivel NOT NULL, inicie com algum valor vlido;
c) Declarar uma varivel que facilite a leitura e manuteno do cdigo;
d) No utilizar uma varivel com o mesmo nome de uma coluna de tabela referenciada no bloco
PL/SQL;
'ica( voc pode inicializar uma varivel utilizando o operador ':=' ou a palavra chave DEFAULT, como
nos exemplos abaixo:
vNome varchar2(30) := 'Seu Madruga';
Ou
vNnome varchar2(30) DEFAULT 'Seu Madruga';
&x%lorando os datatypes
A PL/SQL fornece vrios datatypes que voc pode usar e eles podem ser agrupados em vrias
categorias: datatypes escalares, datatypes de objeto grande, registros e ponteiros.
Neste curso exploraremos os datatypes escalares e os registros.
Datatypes escalares 6ti%os de dados sim%les7
Uma vari)vel escalar uma varivel que no formada de alguma combinao de outras variveis.
As variveis escalares no tm componentes internos que voc pode manipular individualmente. Elas so
usadas para construir datatypes mais complexos, tais como os registros e arrays.
A tabela 1 abaixo exemplifica alguns datatypes escalares da PL/SQL:
Elaborado por Josinei Barbosa da Silva
Pgina 23 de 154
Treinamento
Tabela 1: E1em#lo de datatA#es escalares
Datatype Uso
VARCHAR2 Strings de caractere de comprimento varivel
CHAR Strings de caractere de comprimento fixo
NUMBER Nmeros fixos ou de ponto flutuante
BYNARY_INTEGER Valores de inteiros
PLS_INTEGER Usado para clculos rpidos de inteiros
DATE Datas
BOOLEAN Valores TRUE ou FALSE
LONG Usado para armazenar strings longas de caracteres (obsoleto)
LONG ROW Usado para armazenar grandes quantidades de dados binrios
(obsoleto)
38O9T'4T&+ Alguns datatypes da PL/SQL no possuem equivalentes no banco de dados Oracle,
embora muitos coincidam. O datatype PLS_INTEGER, por exemplo, exclusivo da PL/SQL e outros
possuem pequenas diferenas, como LONG que possui limites inferiores ao do banco de dados.
A PL/SQL tambm fornece subtipos de alguns datatypes. Um subtipo representa um caso especial de
um datatype, geralmente representando um intervalo menor de valores do que o tipo pai. Por exemplo,
POSITIVE um subtipo de BINARY_INTEGER que contm apenas valores positivos. Em alguns casos, os
subtipos s existem para fornecer alternativos por questes de compatibilidade com padro SQL ou com
outras marcas conhecidas de banco de dados do mercado.
Abaixo segue uma tabela com exemplos de outros subtipos:
Tabela 2: E1em#lo de subti#os
Subtipo Subtipo de Uso
VARCHAR VARCHAR2 Apenas para compatibilidade. Uso no recomendado.
STRING VARCHAR2 Apenas para compatibilidade.
DECIMAL NUMBER Igual a NUMBER
DEC NUMBER Igual a DECIMAL
DOUBLE PRECISION NUMBER Igual a NUMBER
NUMERIC NUMBER Igual a NUMBER
REAL NUMBER Igual a NUMBER
INTEGER NUMBER Equivalente a NUMBER(38)
INT NUMBER Igual a INTEGER
SMALLINT NUMBER Igual a NUMBER(38)
FLOAT NUMBER Igual a NUMBER
POSITIVE BINARY_INTEGER Permite que apenas os inteiros positivos sejam
armazenados, at o mximo de 2.147.483.687. Zero
no considerado um valor positivo e, portanto,
no um valor permitido.
NATURAL BYNARY_INTEGER Permite que apenas os nmeros naturais sejam
armazenados, o que inclui o zero. O mximo
2.147.483.687
A declarao de uma varivel escalar muito simples, como pode ser visto no exemplo abaixo:
DECLARE
vDataNasc DATE;
vCodDepto NUMBER(4) NOT NULL :3 56;
vCidade 7ARC)AR4(86) :3 'ITU';
vPI CONSTANT NUMBER :3 8'595:;
vBloqueado BOOLEAN :3 FALSE;
BEGIN
Elaborado por Josinei Barbosa da Silva
Pgina 24 de 154
Treinamento
NULL;
END;
1
38O9T'4T&+ Sempre que uma varivel no inicializada, seu valor inicial ser NULL.
$ *tributo +!,PE
Quando declaramos uma varivel PL/SQL para manipular valores que esto em tabelas, temos que
garantir que esta varivel seja do mesmo tipo e tenha a mesma preciso do campo que ser referenciado.
Se isto no acontecer, um erro PL/SQL ocorrer durante a execuo. Para evitar isto, podemos utilizar o
atributo %TYPE para declarar a varivel de acordo com a declarao de outra varivel ou coluna de uma
tabela. O atributo %TYPE mais utilizado quando o valor armazenado na varivel for um valor derivado de
uma coluna de uma tabela do banco. Para usar este atributo ao invs de um tipo de dado que obrigatrio
na declarao de uma varivel, basta colocar o nome da tabela e a coluna desejada e logo depois, coloque
o atributo %TYPE
Sintaxe:
Identificador Tabela.Nome_Coluna%TYPE;
Quando o bloco compilado, uma atribuio do valor de uma coluna para uma varivel feita, o
PL/SQL verifica se o tipo e o tamanho da varivel compatvel com o tipo da coluna que est sendo usada.
Ento, quando utilizamos o atributo %TYPE para definir uma varivel, garantimos que a varivel sempre
estar compatvel com a coluna, mesmo que o tipo desta coluna tenha sido alterado.
Exemplos:
DECLARE
vProVlMaiorvenda produto.pro_vl_maiorvenda;T<PE;
BEGIN
SELECT p.pro_vl_maiorvenda
INTO vProVlMaiorvenda
FROM produto p
()ERE p.pro_in_codigo 3 5;
END;
1
Neste exemplo, podemos observar duas situaes:
1. No precisamos nos preocupar se o tipo da varivel esta compatvel com o campo da tabela;
2. Garantimos que se alguma alterao for realizada no tipo de dado da coluna, no
precisaremos alterar nada na varivel.
Agora imagine se nesse mesmo cdigo o tipo da varivel fosse NUMBER(9,2) e sem avisar o
desenvolvedor, o responsvel pelos objetos do banco alterasse o tipo da coluna pro_vl_maiorvenda para
NUMBER(11,2)?
O desenvolvedor s saberia quando esse cdigo fosse executado e o PL/SQL exibisse o erro. No
nada bom que o seu cliente receba um erro desses durante o trabalho!
#ari)veis do tipo boolean
A declarao de variveis do tipo boolean no difere em nada dos demais tipos, como pudemos ver
em alguns exemplos anteriores, mas existe um recurso interessante no que se refere a atribuio de valores
Elaborado por Josinei Barbosa da Silva
Pgina 25 de 154
Treinamento
para variveis do tipo boolean.
Essas variveis s recebem os valores TRUE, FALSE e NULL. As expresses aritmticas sempre
retornam TRUE ou FALSE e, portanto, voc pode atribuir uma expresso aritmtica para uma varivel do
tipo boolean.
Vejamos um exemplo:
O seguinte bloco PL/SQL est correto:
DECLARE
-- Declarao de variveis
vValorUltimaVenda produto.pro_vl_ultimavenda;T<PE;
vValorMaiorVenda produto.pro_vl_maiorvenda;T<PE;
vUltimaVendaMaior BOOLEAN :3 FALSE;
BEGIN
-- Comando para recuperar valor da ltima venda e da maior venda do produto
SELECT p.pro_vl_ultimavenda= p.pro_vl_maiorvenda
INTO vValorUltimaVenda= vValorMaiorVenda
FROM produto p
()ERE p.pro_in_codigo 3 5;

-- Condio para definir se ltima venda ou no a maior
IF vValorUltimaVenda 3 vValorMaiorVenda T)EN
vUltimaVendaMaior :3 TRUE;
ELSE
vUltimaVendaMaior :3 FALSE;
END IF;
END;
1
Mas poderia ter sido escrito da seguinte maneira:
DECLARE
-- Declarao de variveis
vValorUltimaVenda produto.pro_vl_ultimavenda;T<PE;
vValorMaiorVenda produto.pro_vl_maiorvenda;T<PE;
vUltimaVendaMaior BOOLEAN :3 FALSE;
BEGIN
-- Comando para recuperar valor da ltima venda e da maior venda do produto
SELECT p.pro_vl_ultimavenda= p.pro_vl_maiorvenda
INTO vValorUltimaVenda= vValorMaiorVenda
FROM produto p
()ERE p.pro_in_codigo 3 5;

-- Atribuir valor da condio para definir se ltima venda ou no a maior
vUltimaVendaMaior :3 (vValorUltimaVenda 3 vValorMaiorVenda);
END;
1
Como pode ser observado, obtemos o mesmo resultado, mas com um cdigo mais limpo.
Ti%o 9e*istro
Um registro uma coleo de valores individuais que esto relacionados de alguma forma. Com
frequncia os registros so usados para representar uma linha de uma tabela, e assim o relacionamento se
baseia no fato de que todos os valores vm da mesma linha. Cada campo de um registro exclusivo e tem
seus prprios valores. Um registro como um todo no tem um valor.
Elaborado por Josinei Barbosa da Silva
Pgina 26 de 154
Treinamento
Usando os registros voc pode agrupar dados semelhantes em uma estrutura e depois manipular sua
estrutura como uma entidade ou unidade lgica. sso ajuda a reproduzir o cdigo, e o mantm mais fcil de
atualizar e entender.
Para usar um registro voc deve defini-lo declarando um tipo de registro. Depois voc deve declarar
uma ou mais variveis PL/SQL como sendo daquele tipo.
Voc declara um tipo de registro na parte da declarao de um bloco, como outra varivel qualquer.
O exemplo abaixo mostra como declarar e usar um tipo registro:
DECLARE
-- Definio de tipos
T<PE TProduto IS RECORD(
Nome 7ARC)AR4(96) =
Marca 7ARC)AR4(46)=
ValorUltimaVenda NUMBER(>=4)
);

-- Declarao de variveis
vProd TProduto;
BEGIN
-- Atribuir valor para o registro vProduto
SELECT p.pro_st_nome= p.pro_st_marca= p.pro_vl_ultimavenda
INTO vProd.Nome= vProd.Marca= vProd.ValorUltimaVenda
FROM produto p
()ERE p.pro_in_codigo 3 5;

-- Imprimir na tela os dados recuperados
dbms_output.put_line('No! %o ,&o%uto2 '##vProd.Nome##chr(56)##
'M$&c$2 '##vProd.Marca##chr(56)##
'7$/o& ?/ti!$ .n%$2 '##to_char(vProd.ValorUltimaVenda)
);
END;
1
38O9T'4T&+ Para que o pacote DBMS_OUTPUT.PUT_LINE imprima o resultado desejado na tela,
necessrio ativar a impresso na ferramenta. No SQL*Plus e no Command Window do PL/SQL Developer
essa funcionalidade ativada atravs do comando SET SERVEROUTPUT ON. No Oracle SQL Developer
basta ativar o boto na aba DBMS Output, da janela Worksheet.
$ *tributo +"$-!,PE
Quando uma varivel de tipo de registro se baseia em uma tabela, isso significa que os campos do
registro tm exatamente o mesmo nome e datatype das colunas da tabela especificada. Voc usa o atributo
%ROWTYPE para declarar um registro com base em uma tabela.
A sintaxe para declarar uma varivel usando o atributo %ROWTYPE a seguinte:
nome_variavel tabela;RO(T<PE
A maior vantagem no uso do atributo %ROWTYPE que uma alterao na definio de tabela se
reflete automaticamente no seu cdigo PL/SQL.
38O9T'4T&+ O acrscimo de uma coluna a uma tabela ser transparente para o seu cdigo
PL/SQL, assim como determinados tipos de alteraes no datatype. Entretanto, quando voc remove uma
coluna (drop column) de tabela que o seu cdigo est usando, voc precisa alter-lo para retirar as
referencias daquela coluna (inclusive nas variveis de registro).
Elaborado por Josinei Barbosa da Silva
Pgina 27 de 154
Treinamento
A seguir, mostramos o exemplo de tipo de registro alterado para usar o atributo %ROWTYPE:
DECLARE
-- Declarao de variveis
vProd produto;RO(T<PE;
BEGIN
-- Atribuir valor para o registro vProduto
SELECT p.*
INTO vProd
FROM produto p
()ERE p.pro_in_codigo 3 5;

-- Imprimir na tela os dados recuperados
dbms_output.put_line('No! %o ,&o%uto2 '##vProd.pro_st_nome##chr(56)##
'M$&c$2 '##vProd.pro_st_marca##chr(56)##
'7$/o& ?/ti!$ .n%$2 '##to_char(vProd.pro_vl_ultimavenda)
);
END;
1
A desvantagem deste exemplo em relao ao anterior, que neste caso todas as colunas da tabela
so recuperadas para a varivel vProd. Dependendo do nmero de colunas que "* retornar, haver
desperdcio de memria.
O &sco%o de uma varivel
Escopo de uma varivel a regio de um programa no qual podemos referenciar a varivel. Variveis
declaradas em blocos PL/SQL so considerados local para o bloco onde est declarado e *lobal para
todos seus sub-blocos. Se uma varivel global redeclarada em um sub-bloco, ambos permanecem no
escopo. Dentro do sub-bloco, somente a varivel local visvel e para referenciar a varivel global devemos
utilizar uma tag, que ser vista a seguir.
Entretanto, no podemos declarar uma varivel duas vezes no mesmo bloco, mas podemos declarar
a mesma varivel em dois blocos diferentes. As duas variveis so distintas, ou seja, qualquer mudana em
uma, no ir afetar a outra.
Um bloco no pode referenciar variveis declaradas em outros blocos de mesmo nvel porque estas
variveis no so locais e nem globais para este bloco.
Uma varivel visvel no bloco no qual ela foi declarada e para todos os sub-blocos e procedures e
functions aninhados. Se o bloco no acha a varivel declarada localmente, ele procura na sesso
declarativa do bloco pai. Um bloco pai nunca procura por uma varivel declarada no bloco filho.
O escopo descrito aqui se aplica a todos objetos declarados, tais como variveis, cursores, excees
definidas por usurio e constantes.
Veja uma representao do escopo de variveis:
Elaborado por Josinei Barbosa da Silva
Pgina 28 de 154
Treinamento
Figura 1-: Esco#o de uma )ari)el
5sando ta*s %ara identi1icar um varivel
Podemos identificar uma varivel global dentro de um sub-bloco usando uma tag como prefixo do
bloco PL/SQL. Fazendo isto, conseguimos com que uma varivel declarada no bloco pai e de mesmo nome
da varivel declarada no bloco filho, possa ser manipulada dentro do bloco filho. Esta tag definida atravs
dos smbolos << tag >>, onde tag pode ser qualquer palavra, no podendo utilizar nmeros, nem outros
smbolos.
Vejamos um exemplo prtico:
@@B/ocoP$iAA
DECLARE
x nu!B& :3 9;
BEGIN
DECLARE
x nu!B& :3 4;
BEGIN
dbms_output.put_line(B/ocoP$i.x);
END;

dbms_output.put_line(x);
END;
1
No exemplo acima, estamos referenciando a varivel x do bloco pai, dentro da sesso executvel do
bloco filho, mesmo o bloco filho tendo uma varivel de mesmo nome. sto s foi possvel porque utilizamos a
tag BlocoPai, que est pr-fixada no bloco pai.
:alores 4ulos
comum que os desenvolvedores menos experientes confundam NULL com 0 (zero) ou espao em
branco. SSO UM ERRO! E um erro grave, pois pode acarretar em falha do programa.
NULL no um valor propriamente dito. NULL igual a NADA!
Quando uma varivel possui valor NULL, quer dizer que no foi atribudo valor algum a essa varivel
(ou atriburam NULL). O mesmo vale para o preenchimento de colunas de tabelas do banco de dados
Oracle. Se voc executa uma declarao SELECT para recuperar o valor de determinada coluna e, para
algumas linhas retornadas, o valor exibido NULL, quer dizer que nada foi gravado naquela coluna para
aquele registro.
Elaborado por Josinei Barbosa da Silva
Pgina 29 de 154
Treinamento
Valores NULL prejudicam, diretamente, expresses aritmticas e condies booleanas (que retornam
verdadeiro ou falso), pois o resultado de qualquer soma ou subtrao ou diviso ou multiplicao entre um
valor qualquer e NULL, ser NULL.
Portanto, tome muito cuidado em utilizar variveis cujo contedo seja NULL. Esse o valor inicial de
qualquer varivel, antes da inicializao.
Elaborado por Josinei Barbosa da Silva
Pgina 30 de 154
Treinamento
Tratamento de excees
Eventualmente o servidor Oracle ou o aplicativo do usurio causa um erro durante o processamento
em runtime. Tais erros podem surgir de falhas de hardware ou rede, erros lgicos de aplicativo, erros de
integridade de dados e de muitas outras fontes. Esses erros so conhecidos como excees, ou seja, esses
eventos indesejados so excees do processamento normal e esperado.
(uncionamento *eral
Geralmente, quando um erro ocorre, o processamento do bloco PL/SQL imediatamente encerrado.
O processamento corrente no concludo. A Oracle permite que voc esteja preparado para esses erros e
implemente lgica nos programas para lidar com os erros, permitindo que o processamento continue. Essa
lgica implementada para gerenciar os erros conhecida como c/digo de tratamento de e0ce12es. Com o
tratamento de excees da Oracle, quando um erro detectado, o controle passado para a parte de
tratamento de excees do programa e o processamento completado normalmente. O tratamento de erros
tambm fornece informaes valiosas para depurar os aplicativos e para proteger melhor o aplicativo contra
erros futuros.
Sem um recurso para tratamento de excees, um programa deve verificar as excees a cada
comando, como mostrado a seguir:
DECLARE
vMeta NUMBER :36;
vRealizado NUMBER :356666;
vPercentual NUMBER :36;
BEGIN
-- Recuperar meta do vendedor para o ms corrente
SELECT r.rep_vl_metamensal
INTO vMeta
FROM representante r
()ERE r.rep_in_codigo 3 56;
/* S acha percentual se meta for maior que zero
para no gerar erro de diviso por zero
*/
IF vMeta A 6 T)EN
vPercentual :3 (vRealizado 1 vMeta) * 566;
dbms_output.put_line('O &,&snt$nt CD &$/iEou '##to_char(vPercentual)##
' % su$ !t$''
);
ELSE
vPercentual :3 6;
dbms_output.put_line('O &,&snt$nt n*o ,ossui !t$'');
END IF;
END;
1
Se o tratamento de excees do PL/SQL fosse usado no cdigo acima, no seria necessrio utilizar a
estrutura IF...ELSE...END IF, deixando assim o cdigo mais limpo e evitando uma verificao
obrigatria, mesmo quando a expresso no fosse gerar um erro.
Existem trs tipos de excees na PL/SQL:
Erros predefinidos da Oracle;
Erros no definidos da Oracle;
Elaborado por Josinei Barbosa da Silva
Pgina 31 de 154
Treinamento
Erros definidos pelo usurio.
Ca%turando uma exceo
Se a exceo invocada na rea de execuo do bloco e a mesma for manipulada na rea de
exceo com sucesso, ento a execuo no propaga para o bloco ou para o ambiente e o bloco termina
com sucesso.
Podemos capturar qualquer erro incluindo comandos dentro da rea de exceo de um bloco
PL/SQL. Cada manipulao consiste de uma clusula WHEN que especifica uma exceo, seguida por uma
seqncia de comandos que sero executados quando a exceo for invocada.
Sintaxe:
EXCEPTION
()EN exceo1 [OR exceo2 ...] T)EN
Comando1;
Comando2;
. . .
()EN exceo3 [OR exceo4 ...] T)EN
Comando1;
Comando2;
. . .
()EN OT)ERS T)EN
Comando1;
Comando2;
38O9T'4T&:
A rea de manipulao de excees, inicia-se, sempre, com a palavra chave EXCEPTION.
O desenvolvedor deve, sempre, definir um manipulador para cada possvel exceo dentro
do bloco PL/SQL;
Quando uma exceo ocorre, o PL/SQL processa somente um manipulador antes de deixar o
bloco.
Coloque a clusula OTHERS depois de todas as clusulas de manipulao de exceo, pois o
seu manipulador ser executado caso nenhum outro manipulador corresponda a exceo gerada;
Podemos ter somente uma clusula OTHERS dentro de uma rea de tratamento de excees;
Excees no podem aparecer dentro de comandos SQL.
&xcees %r;de1inidas da Oracle
O servidor Oracle define vrios erros com nomes padro. Embora todo erro Oracle tenha um nmero,
um erro deve ser referenciado pelo nome. A PL/SQL tem alguns erros e excees comuns da Oracle
predefinidos, os quais incluem o seguinte:
Tabela 3: E1ce29es #r?%deinidas da 'racle
Excees Descrio
no_data_found A SELECT de linha nica no retornou dados.
Elaborado por Josinei Barbosa da Silva
Pgina 32 de 154
Treinamento
Excees Descrio
too_many_rows A SELECT de linha nica retornou mais de uma linha.
invalid_cursor Houve a tentativa de operao ilegal de cursor.
value_error Ocorreu um erro de aritmtica, converso, truncagem ou
restrio.
invalid_number A converso de uma string para um nmero, falhou.
zero_divide Ocorreu uma tentativa de dividir por zero.
dup_val_on_index Houve uma tentativa de inserir, em duplicata, um valor em uma
coluna (ou um conjunto de colunas) que possui um ndice
exclusivo (UNIQUE KEY ou PRIMARY KEY).
cursor_already_open Houve uma tentativa de abrir um cursor que foi aberto
anteriormente.
not_logged_on Uma chamada de banco de dados foi feita sem o usurio estar
conectado ao Oracle.
transaction_backed_out Uma parte remota de uma transao teve rollback.
login_danied Um login no banco de dados Oracle falhou por causa de um nome de
usurio e/ou senha invlidos.
program_error A PL/SQL encontrou um problema interno.
storage_error A PL/SQL ficou sem memria ou a memria est corrompida.
timeout_on_resource Um timeout ocorreu enquanto o Oracle estava esperando por um
recurso
rowtype_mismatch Uma varivel de cursor no incompatvel com a linha de cursor
others Uma declarao catchall que detecta um erro que no foi
detectado nas excees anteriores
Exemplo:
BEGIN
. . .
EXCEPTION
()EN NO_DATA_FOUND T)EN
Comando1;
Comando2;
. . .
()EN TOO_MANY_ROWS T)EN
Comando1;
Comando2;
. . .
()EN OT)ERS T)EN
Comando1;
Comando2;
END;
1
&rros inde1inidos da Oracle
Conseguimos capturar erros no definidos pelo Servidor Oracle declarando-os primeiro ou usando o
manipulador OTHERS. A exceo declarada invocada implicitamente. No PL/SQL, o PRAGMA
EXCEPTION_INIT() "diz para o compilador para associar uma exceo com um nmero de erro Oracle.
sto permite referenciar em qualquer exceo interna pelo nome e escrever um manipulador especfico para
isto.
38O9T'4T&+ PRAGMA uma palavra reservada e uma diretiva de compilao que no
processada quando um bloco executado. Ele direciona o compilador do PL/SQL para interpretar todas
Elaborado por Josinei Barbosa da Silva
Pgina 33 de 154
Treinamento
ocorrncias de nomes de exceo dentro de um bloco como nmero de erro do servidor Oracle.
Exemplo:
DECLARE
-- Definio de excees
eResumoDependente EXCEPTION;
-- Associar exceo definida ao erro do Oracle
PRAGMA EXCEPTIONFINIT (eResumoDependente=G44>4);
BEGIN
-- Excluir cliente
DELETE cliente
()ERE cli_in_codigo 3 56;
COMMIT;
EXCEPTION
-- Tratar erro de dependncia entre a tabela RESUMO_MENSAL_VENDA
()EN eResumoDependente T)EN
ROLLBACH;
dbms_output.put_line('O C/int 56 n*o ,o% s& xc/u-%o= ,ois xist &su!o
% .n%$s .incu/$%o'');
END;
1
(Para o servidor Oracle, o erro -2292 uma violao de integridade)
&rros de1inidos %elo usurio
Um usurio pode levantar explicitamente uma exceo usando o comando RAISE. Esse
procedimento deve ser usado apenas quando a Oracle no levanta sua prpria exceo ou quando o
processamento no desejado ou impossvel de ser completado.
As etapas para levantar e tratar um erro definido pelo usurio, so os seguintes:
1. Declarar o nome para a exceo de usurio dentro da seo de declarao do bloco;
2. Levantar a exceo explicitamente dentro da parte executvel do bloco, usando o comando
RAISE;
3. Referenciar a exceo declarada com uma rotina de tratamento de erro.
O exemplo seguinte ilustra o uso da exceo definida pelo usurio:
DECLARE
-- Declarar exceo
eRegistroInexistente xc,tion;
BEGIN
-- Excluir cliente
DELETE cliente c
()ERE c.cli_in_codigo 3 5666;

-- Se cliente no existe, gerar exceo
IF SIL;NOTFOUND T)EN
&$is eRegistroInexistente;
END IF;

-- Validar excluso
Elaborado por Josinei Barbosa da Silva
Pgina 34 de 154
Treinamento
COMMIT;
EXCEPTION
-- Se registro no existe, informa usurio
()EN eRegistroInexistente T)EN
ROLLBACH;
dbms_output.put_line('C/int 5666 n*o xistJ'##chr(56)##
'NnKu! &List&o +oi xc/u-%o''
);

END;
1
S#!COD& e S#!&998
Quando uma exceo ocorre, podemos identificar o cdigo do erro ou a mensagem de erro usando
duas funes. Baseados nos valores do cdigo ou mensagem, podemos decidir qual ao tomar baseada
no erro.
As funes so:
SILCODE+ Retorna um valor numrico para o cdigo de erro.
SILERRM+ Retorna um caractere contendo a mensagem associada com o nmero do erro.
O uso dessas funes pode ser muito til quando a exceo detectada com a clusula WHEN
OTHERS, a qual usada para detectar excees no previstas ou desconhecidas.
At a verso do Oracle Database 8i, voc no pode usar SQLCODE e SQLERRM diretamente em uma
declarao SQL. Em vez disso, voc deve atribuir seus valores s variveis locais e depois usar essas
variveis na declarao SQL (nas verses seguintes, isso j possvel).
A funo SQLCODE retorna o cdigo de erro da exceo.
SQLERRM, retorna a mensagem de erro correspondente o cdigo de erro da exceo.
Quando nenhuma exceo foi levantada, o valor de SQLCODE 0 (zero) e quando a exceo foi
declarada pelo usurio, o valor 1;
Abaixo, segue um exemplo de uso das funes SQLCODE e SQERRM:
DECLARE
-- Declarao de variveis
vCodeError NUMBER :3 6;
vMessageError 7ARC)AR4(4::) :3'';

-- Declarar exceo
eRegistroInexistente xc,tion;
BEGIN
-- Excluir cliente
DELETE cliente c
()ERE c.cli_in_codigo 3 5666;

-- Se cliente no existe, gerar exceo
IF SIL;NOTFOUND T)EN
&$is eRegistroInexistente;
Elaborado por Josinei Barbosa da Silva
Pgina 35 de 154
Treinamento
END IF;

-- Validar excluso
COMMIT;
EXCEPTION
-- Se registro no existe, informa usurio
()EN eRegistroInexistente T)EN
ROLLBACH;
dbms_output.put_line('C/int 5666 n*o xistJ'##chr(56)##
'NnKu! &List&o +oi xc/u-%o''
);
()EN OT)ERS T)EN
ROLLBACH;
vCodeError :3 SILCODE;
vMessageError :3 SILERRM;

-- Imprime na tela o cdigo do erro e a mensagem
dbms_output.put_line('CM%'E&&o2 '##to_char(vCodeError)##chr(56)##
'Mns$L!2 '##vMessageError
);
END;
1
Como pode ser visto no exemplo acima, caso no exista o registro a ser excludo, o bloco levanta a
exceo eRegistroInexistente e se ocorrer qualquer outra exceo, executado um ROLLBACK e o
cdigo do erro, mais sua descrio, so exibidos na tela.
O %rocedimento raise<a%%lication<error
Usando o procedimento RAISE_APPLICATION_ERROR, podemos comunicar uma exceo pr-
definida retornando um cdigo de erro e uma mensagem de erro no padro. Com isto, podemos retornar
erros para a aplicao e evitar excees no manipuladas.
Sintaxe:
raise_application_error(num_erro, mensagem[,{TRUE | FALSE }]);
Onde:
num_erro nmero pr-definido pelo usurio para excees entre -20000 e -20999;
mensagem uma mensagem pr-definida pelo usurio para a exceo. Pode ser um string
de at 2048 bytes;
TRUE|FALSE um parmetro booleano opcional. Se TRUE, o erro colocado na fila dos
prximos erros e se for FALSE, o padro, o erro substitui todos os erros precedentes.
Exemplo:
DECLARE
-- Declarar exceo
eRegistroInexistente xc,tion;
BEGIN
-- Excluir cliente
DELETE cliente c
()ERE c.cli_in_codigo 3 5666;

Elaborado por Josinei Barbosa da Silva
Pgina 36 de 154
Treinamento
-- Se cliente no existe, gerar exceo
IF SIL;NOTFOUND T)EN
&$is eRegistroInexistente;
END IF;

-- Validar excluso
COMMIT;
EXCEPTION
-- Se registro no existe, informa usurio
()EN eRegistroInexistente T)EN
ROLLBACH;
raise_application_error(G46566='C/int 5666 n*o xistJ'##chr(56)##
'NnKu! &List&o +oi xc/u-%o''
);
()EN OT)ERS T)EN
ROLLBACH;
raise_application_error(G46565='Oco&&u u! &&o n*o i%nti+ic$%o'##chr(56)##
'$o tnt$& xc/ui& o c/int 5666'');
END;
1
38O9T'4T&+ O procedimento RAISE_APLICATION_ERROR pode ser invocado do bloco principal
(no exclusividade da rea de exceo)
9e*ras de esco%o de exceo
Voc precisa conhecer as orientaes abaixo sobre o escopo das declaraes:
Uma exceo no pode ser declarada duas vezes no mesmo bloco, mas a mesma exceo
pode ser declarada em dois blocos diferentes;
As excees so locais ao bloco onde elas foram declaradas e globais a todos os sub-blocos
do bloco. Os blocos includos (bloco pai) no podem referenciar as excees que foram declaradas
em nenhum de seus sub-blocos;
As declaraes globais podem ser declaradas de novo no nvel do sub-bloco local. Se isso
ocorrer, a declarao local tem precedncia sobre a declarao global.
ro%a*ando as excees
Quando um erro encontrado, a PL/SQL procura o tratamento de exceo apropriado no bloco atual.
Se nenhum tratamento estiver presente, ento a PL/SQL propaga o erro para o bloco includo (bloco pai).
Se nenhum tratamento for encontrado l, o erro propagado para os blocos includos at que um
tratamento seja encontrado. Esse processo pode continuar at o ambiente host receber e lidar com o erro.
Por exemplo, se o SQL*Plus recebeu um erro no tratado de um bloco PL/SQL, o SQL*Plus lida com esse
erro exibindo o cdigo de erro e a mensagem na tela do usurio.
Elaborado por Josinei Barbosa da Silva
Pgina 37 de 154
Treinamento
&xerc=cios %ro%ostos
1. Escreva um bloco PL/SQL que recupere a quantidade de registros de uma tabela qualquer e
imprima essa quantidade na tela, usando o SQL*Plus. Salve esse bloco em um arquivo do sistema
operacional e execute-o novamente no SQL*Plus, usando o comando @.
2. Edit o bloco escrito no exerccio anterior no ommand -indo3 do PL/SQL Developer e
execute-o.
3. Escreva um bloco PL/SQL que declare uma varivel e exiba o valor. Depois, adicione um
bloco aninhado que declara uma varivel de mesmo nome e exiba seu valor.
4. Escreva um bloco que declare uma varivel do tipo date para guardar a data de nascimento
de uma pessoa. Atribua uma data para essa varivel, calcule a idade da pessoa com base na data
atual (SYSDATE) e imprima essa idade em anos. Dica: Uma subtrao entre datas, em PL/SQL,
retorna a diferena em dias.
5. Altere o bloco escrito no exerccio quatro, eliminando a varivel criada para armazenar a data
de nascimento e criando um tipo que guarde: o nome da pessoa, a data de nascimento e a idade.
Defina uma varivel do tipo criado. Logo no incio do bloco, atribua o nome da pessoa e a data de
nascimento para a varivel. Aps calcular a idade da pessoa, atribua o resultado para o campo de
idade da varivel. Por fim, imprima na tela o nome da pessoa, a data de nascimento e a sua idade.
6. Escreva um bloco PL/SQL que recupere dados de uma tabela qualquer e armazene seu
retorno em uma varivel do tipo registro. O bloco deve conter uma rea de exceo para tratar os
seguintes erros:
a) Muitas linhas retornadas pela instruo SQL;
b) Nenhuma linha retornada pela instruo SQL;
c) Outros erros.
Elaborado por Josinei Barbosa da Silva
Pgina 38 de 154
Treinamento
02 Estruturas PL/SQL
1. Cenrio %ara %rtica
2. Declaraes 3( e !OOs;
3. 5tili>ao de cursores;
4. Blocos an?nimos2 %rocedimentos e 1unes@
Elaborado por Josinei Barbosa da Silva
Pgina 39 de 154
Treinamento
Cenrio %ara %rtica
At agora vimos muitas explicaes e exemplos, mas praticamos pouco.
Nos prximos captulos precisaremos exercitar o que for visto e, de preferncia, num ambiente de
teste que simule algum sistema, mesmo que simplrio.
Para facilitar isso, usaremos um modelo de dados de um sistema de vendas bem simples, que no
chega a normalizao ideal, porm, atende perfeitamente s nossas necessidades.
8odelo lA*ico de entidade e relacionamento
Abaixo segue o nosso modelo lgico de entidade e relacionamento:
Figura 1/: $odelo LBgico de Entidade >elacionamento do sistema de )endas
Elaborado por Josinei Barbosa da Silva
Pgina 40 de 154
Treinamento
8odelo 1=sico de entidade e relacionamento
Para entendermos como as entidades se relacionam, o modelo lgico a melhor opo, pois fornece
uma representao mais limpa, porm, quando precisamos de detalhes de implementao fsica, como o
tipo de uma coluna para elaborar uma instruo SQL, a melhor representao o modelo fsico de entidade
e relacionamento, como na figura a seguir:
Figura 14: $odelo FCsico de Entidade >elacionamento do sistema de )endas
De agora em diante, procuraremos elabora os exerccios com base neste modelo.
A criao dos objetos e a criao da massa de dados encontra-se no arquivo TreinamentoPLSQL.sql.
Elaborado por Josinei Barbosa da Silva
Pgina 41 de 154
Treinamento
Crie a tablespace USERS, caso ainda no exista, e crie um schema (USER) com o seu nome para
ser o proprietrio dos objetos.
Elaborado por Josinei Barbosa da Silva
Pgina 42 de 154
Treinamento
Declaraes 3( e !OOs
' declarao 3(
A declarao IF permite avaliar uma ou mais condies, como em outras linguagens.
A sintaxe de uma declarao IF no PL/SQL a seguinte:
IF @condioA T)EN
@comandos a serem executadosA
END IF;
Nessa sintaxe, condi14o a condio que voc quer verificar. Se a condio for avaliada como
TRUE, ou seja, verdadeira, os comandos de execuo sero executados.
Como exemplo, abaixo temos um bloco PL/SQL que gera uma exceo se no existirem clientes
cadastrados:
DECLARE
vQtdeClientes INTEGER;
BEGIN
-- Recuperar quantidade de clientes
SELECT COUNT(*)
INTO vQtdeClientes
FROM cliente;

-- Se no existir clientes gera uma exceo
IF vQtdeClientes 3 6 T)EN
raise_application_error(G46566='N*o xist! c/ints c$%$st&$%os');
END IF;
END;
1
' declarao 3(@@@TB&4@@@&!S&
No exemplo anterior voc no se importou com os resultados quando existem clientes. A declarao
IF...THEN...ELSE permite processar uma srie de declaraes abaixo de ELSE se a condio for falsa.
A sintaxe da declarao IF...THEN...ELSE :
IF @condioA T)EN
@comandos a serem executados para condio verdadeiraA
ELSE
@comandos a serem executados para condio verdadeiraA
END IF;
Nessa sintaxe, se a condio for FALSE, executado os comandos abaixo de ELSE.
Para exemplificar o uso da declarao F...THEN...ELSE, vamos modificar o exemplo anterior para
gerar uma exceo quando no existir clientes ou imprimir na tela a quantidade de clientes cadastrados
quando existir:
DECLARE
vQtdeClientes INTEGER;
BEGIN
-- Recuperar quantidade de clientes
SELECT COUNT(*)
INTO vQtdeClientes
FROM cliente;
Elaborado por Josinei Barbosa da Silva
Pgina 43 de 154
Treinamento

-- Se no existir clientes gera uma exceo
IF vQtdeClientes 3 6 T)EN
raise_application_error(G46566='N*o xist! c/ints c$%$st&$%os');
ELSE
dbms_output.put_line('Exist! '##to_char(vQtdeClientes)## ' c$%$st&$%os'');
END IF;
END;
1
Assim como em outras linguagens, voc pode aninhar as declaraes IF e IF...THEN...ELSE,
como mostrado abaixo:
IF @condio_01A T)EN
IF @condio_02A T)EN
...
...
ELSE
IF @condio_03A T)EN
...
...
END IF;
...
...
END IF;
...
...
END IF;
' declarao 3(@@@&!S3(
Voc pode imaginar as declaraes F aninhadas como executando um AND lgico mas ELSF como
executando um OR lgico. O recurso bom no uso de ELSF no lugar de F aninhado que fica muito mais
fcil seguir a lgica da declarao ELSF porque voc pode identificar facilmente quais declaraes ocorrem
em quais condies lgicas, como pode ser visto abaixo:
IF @condio_01A T)EN
IF @condio_02A T)EN
...
...
ELSIF @condio_03A T)EN
...
...
ELSIF @condio_04A T)EN
...
...
ELSE
...
END IF;
END IF;
Como podemos observar, ao final de uma seqncia de ELSIF, podemos ter um ELSE. sso quer
dizer que se nenhuma das condies IF ou ELSIF for atendida, os comandos abaixo de ELSE sero
executados.
Elaborado por Josinei Barbosa da Silva
Pgina 44 de 154
Treinamento
!oo% Sim%les
De todos os loops, esse o mais simples de usar e entender. Sua sintaxe a seguinte:
LOOP
@@DeclaraesAA
END LOOP;
Como vemos na sintaxe, esse loop no tem uma clusula de sada, ou seja, ele infinito. Para
abandonar o loop podemos utilizar duas declaraes: EXIT ou EXIT WHEN.
Exemplo de loop simples:
BEGIN
LOOP
NULL;
EXIT;
END LOOP;
END;
1
O exemplo acima no faz nada, mas suficiente para exemplificar o funcionamento de um loop
simples.
Criando um loo% 9&&'T@@@54T3!
Em PL/SQL no existe um loop do tipo REPET...UNTIL incorporado, entretanto, voc pode simular
um usando o loop simples e as declaraes EXIT ou EXIT WHEN.
A sintaxe de um loop REPEAT...UNTIL, em PL/SQL, seria da seguinte forma:
LOOP;
@DeclaraesA
IF @condicaoA T)EN
EXIT;
END IF;
END LOOP;
Alternativamente voc pode usar o mtodo mais comum, que :
LOOP;
@DeclaraesA
EXIT ()EN @condioA;
END LOOP;
!oo% (O9
Loop FOR tm a mesma estrutura de um loop simples. A diferena que possui um comando de
controle antes da palavra chave LOOP, para determinar o nmero de iteraes que o PL/SQL ir realizar.
Sintaxe:
FOR contador IN [RE7ERSE] limite_inferior..limite_superior LOOP
Comando1;
Comando2;
END LOOP;
Onde:
ontador uma declarao inteira implcita cujo valor incrementado ou decrementado(se a palavra
Elaborado por Josinei Barbosa da Silva
Pgina 45 de 154
Treinamento
chave REVERSE for usada) automaticamente em 1 em cada iterao do loop, at o limite inferior ou limite
superior ser alcanado. No preciso declarar nenhum contador, ele declarado implicitamente como um
inteiro.
38O9T'4T&+ A seqncia de comandos executado cada vez que o contador incrementado,
como determinado pelos dois limites inferior e superior.
Os limites inferior e superior podem ser literais, variveis ou expresses, mas devem ser valores
inteiros. Se o limite inferior for um valor maior que o limite superior, a seqncia de comandos no ser
executada.
Exemplo de um loop que vai de 1 5 e imprime na tela o nmero de cada loop:
BEGIN
FOR LoopCount IN 5..: LOOP
dbms_output.put_line('Loo, '## to_char(LoopCount));
END LOOP;
END;
1
MPORTANTE: O Oracle no fornece opes para passar por um loop com um incremento que no
seja um e o valor do contador no pode ser alterado durante o loop. Voc pode escrever loops que so
executados com um incremento diferente executando as declaraes apenas quando determinada condio
verdadeira. O exemplo abaixo demonstra como incrementar por um valor de 2:
BEGIN
FOR vLoopCount IN 5..N LOOP
IF MOD(vLoopCount=4) 3 6 T)EN
dbms_output.put_line('Cont$%o& O iLu$/ $ '##to_char(vLoopCount));
END IF;
END LOOP;
END;
1
Quando executar esse bloco, o resultado deve ser o seguinte:
Contador igual a 2
Contador igual a 4
Contador igual a 6
Este exemplo mostra apenas uma das vrias maneiras pelas quais voc pode incrementar um loop. A
uno MOD, neste caso, simplesmente testa para ter certeza de que o nmero divisvel igualmente por
uma valor de 2. Voc pode alterar isso, facilmente, para 3, 5 ou o que quiser incrementar. Para decremento
basta adicionar a palavra-chave REVERSE.
!oo% -B3!&
Podemos usar o Loop WHILE para repetir uma seqncia de comandos enquanto a condio de
controle for TRUE. A condio avaliada no incio de cada iterao. O loop termina quando a condio for
FALSE. Se a condio for FALSE no incio do Loop, ento nenhuma iterao ser realizada.
Sintaxe:
()ILE condio LOOP
Elaborado por Josinei Barbosa da Silva
Pgina 46 de 154
Treinamento
Comando1;
Comando2;
END LOOP;
Se a varivel utilizada na condio no mudar durante o corpo do loop, ento a condio ir
permanecer TRUE e o loop nunca terminar.
Se a condio retornar NULL, o loop ser encerrado e o controle passar para o prximo comando.
Abaixo temos o exemplo de um loop que nunca ser executado:
DECLARE
vCalc NUMBER :3 6;
BEGIN
()ILE vCalc A3 56 LOOP
vCalc :3 vCalc P 5;
dbms_output.put_line('O .$/o& % .C$/c O '##vCalc);
END LOOP;
END;
1
E abaixo uma verso corrigida do exemplo anterior, para que o loop seja executado:
DECLARE
vCalc NUMBER :3 6;
BEGIN
()ILE vCalc @3 56 LOOP
vCalc :3 vCalc P 5;
dbms_output.put_line('O .$/o& % .C$/c O '##vCalc);
END LOOP;
END;
1
#ual loo% devo usar$
Todas essas opes de loops podem causar confuso! Como foi visto nos exemplos, voc pode usar
as declaraes FOR, WHLE e LOOP para criar a mesma sada. Entretanto, a Tabela 5 mostra algumas
orientaes gerais sobre quando usar qual tipo de loop.
Tabela 4:7i#o de Loo# a ser usado
Loop Quando usar
LOOP (Simples) Voc pode usar o LOOP simples se quiser criar um loop do tipo
REPEAT...UNTIL. O LOOP simples perfeito para executar essa
tarefa
FOR Use sempre o loop FOR se voc souber especificamente quantas
vezes o loop deve ser executado. Se tiver de codificar uma
declarao EXIT ou EXIT WHEN em um loop FOR, voc pode
reconsiderar seu cdigo e usar um loop simples ou uma abordagem
diferente.
WHILE Use este loop quando voc pode nem querer executar o loop uma
vez. Embora voc possa duplicar esse resultado em um loop FOR
usando EXIT ou EXIT WHEN, essa situao mais adequada para o
loop WHILE. O loop WHILE o loop mais usado porque ele fornece
mais flexibilidade
Elaborado por Josinei Barbosa da Silva
Pgina 47 de 154
Treinamento
5tili>ao de Cursores
Os cursores da PL/SQL fornecem um modo pelo qual seu programa pode selecionar vrias linhas de
dados do banco de dados e depois processar cada linha individualmente. Especificamente, um cursor um
nome atribudo pela Oracle para cada declarao SQL que processada. Esse nome fornece Oracle um
meio de orientar e controlar todas as fases do processamento da SQL.
Conceitos bsicos
Existem dois tipos de cursores:
Cursor 3m%l=cito+ so cursores declarados pelo PL/SQL implicitamente para todos comandos
DML e comandos SELECT no PL/SQL, independente da quantidade de registros processadas. Ele
precisa fazer isso para gerenciar o processamento da declarao SQL.
Cursor &x%l=cito+ Cursores definidos pelo usurio para manipular registros recuperados por
declaraes SELECT.
Cursores &x%l=citos
Utilizamos cursores explcitos para processar individualmente cada linha retornada por um comando
SELECT. O conjunto de linhas retornadas pelo comando SELECT chamado de con5unto ativo.
Um programa PL/SQL abre o cursor, processa as linhas retornadas pela consulta e depois fecha este
cursor. O cursor marca a posio corrente dentro do conjunto ativo.
Usando cursores explcitos, o programador pode manipular cada registro recuperado por uma
declarao SELECT.
Abaixo vemos o ciclo de vida de um cursor:
Figura 20: .iclo de )ida de um cursor
1. Declare o cursor nomeando o mesmo e defina a estrutura da consulta que ser realizada por
ele;
2. Atravs do comando OPEN, o cursor aberto e executa a consulta que recuperar o conjunto
ativo do banco de dados;
3. Com o comando FETCH, capturamos os dados do registro corrente para utilizarmos em nosso
programa. A cada comando FETCH, devemos testar se ainda existe registro no cursor e abandonar
o LOOP do cursor atravs do comando EXIT, caso no exista (mais adiante veremos como verificar
isso);
4. Quando a manipulao dos dados do cursor for finalizada, ao abandonar o LOOP do cursor,
devemos fech-lo atravs do comando CLOSE, que libera as linhas do cursor;
Declarando um cursor
Elaborado por Josinei Barbosa da Silva
Pgina 48 de 154
Treinamento
Declaramos um cursor utilizando o comando C59SO9@ Podemos referencia variveis na query, mas
elas precisam ser declaradas antes do comando CURSOR.
Sintaxe:
CURSOR nome_cursor IS
Comando_select;
Onde:
nome_cursor um indentificador PL/SQL.
omando_select um comando SQL sem a clusula NTO.
Observaes:
No colocar a clusula NTO na declarao de um cursor, pois ela aparecer depois no
comando FETCH.
Um cursor pode ser qualquer comando SELECT, incluindo joins e outros.
Exemplo:
DECLARE
CURSOR cs_representante IS
SELECT r.rep_in_codigo= r.rep_st_nome
FROM representante r;
CURSOR cs_notafiscal IS
SELECT *
FROM nota_fiscal_venda;
BEGIN
NULL;
END;
1
No exemplo acima, declaramos dois cursores: cs_representante e cs_notafiscal. Em
cs_representante ser recuperado o cdigo e o nome de todos os representantes. No cursor
cs_notafiscal, ser recuperado todos os dados de todas as notas fiscais..
'brindo um Cursor
O comando OPEN executa a consulta associada ao cursor e posiciona o cursor antes da primeira linha
do conjunto ativo.
Sintaxe:
OPEN nome_cursor;
Onde:
nome_cursor o nome previamente declarado do cursor.
OPEN um comando executvel que realiza as seguintes operaes:
Aloca dinamicamente rea de memria para processamento;
Faz o PARSE do comando SELECT;
Elaborado por Josinei Barbosa da Silva
Pgina 49 de 154
Treinamento
Coloca valores nas variveis de input obtendo seus endereos de memria;
dentifica o conjunto de linhas que a consulta retornou. (As linhas retornadas no so
colocadas nas variveis quando o comando OPEN executado, isto acontece quando o
comando FETCH utilizado);
Posiciona o ponteiro antes da primeira linha do conjunto ativo.
38O9T'4T&+ Se a consulta no retornar linhas quando o cursor for aberto, o PL/SQL no retorna
nenhuma exceo. Entretanto, podemos testar o status do cursor depois de cada comando FETCH usando o
atributo de cursor SQL%ROWCOUNT, que ser estudado mais adiante.
9ecu%erando dados de um Cursor
O comando FETCH recupera linhas da consulta associada ao cursor. Para cada comando FETCH
executado, o cursor avana para a prxima linha no conjunto ativo.
Sintaxe:
FETC) nome_cursor INTO [varivel1= varivel2= ...] 1 nome_registro];
Onde:
nome_cursor o nome do cursor declarado na sesso DECLARE;
vari)veln o nome da varivel declarada para armazenar os valores retornados no FETCH;
nome_registro o nome do registro no qual os dados retornados so armazenados. Esta
varivel pode ser declarada usando o atributo %ROWTYPE ou uma varivel de tipo registro;
38O9T'4T&+
A clusula INTO deve conter:
uma varivel para cada coluna retornada pelo comando FETCH, na ordem
correspondente das colunas retornadas e com o mesmo tipo retornado;
Ou um Tipo Registro com o mesmo nmero de colunas do registro retornado (e do
mesmo tipo das colunas do cursor;
Ou uma varivel declarada com %ROWTYPE baseada no prprio cursor.
Valide se o cursor contm linhas. Se quando executar o comando FETCH, o cursor estiver
vazio, no teremos nenhuma linha para processar e nenhum erro ser exibido;
O comando FETCH s pode ser executado se o Cursor estiver aberto.
Exemplo:
DECLARE
-- Declarao de variveis
vCodigoRep representante.rep_in_codigo;tQ,;
vNomeRep representante.rep_st_nome;tQ,;
-- Declarao de cursores
CURSOR cs_representante is
SELECT rep_in_codigo= rep_st_nome
FROM representante;
BEGIN
Elaborado por Josinei Barbosa da Silva
Pgina 50 de 154
Treinamento
-- Abre cursor
OPEN cs_representante;

-- Executa um loop com 10 ciclos
FOR i IN 5..56 LOOP
-- Extrai dados o registro corrente do cursor e avana para o prximo
FETC) cs_representante INTO vCodigoRep = vNomeRep;
-- Imprime dados extrados na tela
dbms_output.put_line(vCodigoRep##' G '##vNomeRep);
END LOOP;
END;
1
No exemplo acima, o programa imprime na tela 10 representantes, um a um.
(ec/ando o Cursor
O comando CLOSE desabilita o cursor e, conseqentemente, o conjunto ativo torna-se indefinido.
Sempre que o processamento do comando SELECT estiver completo, feche o cursor, isto permite que o
cursor seja reaberto quando preciso.
Sintaxe:
CLOSE nome_cursor;
Se for aplicado o comando FETCH em um cursor fechado, a exceo INVALID_CURSOR ocorrer.
O comando CLOSE libera a rea de memria alocada para o cursor. possvel terminar um bloco
PL/SQL sem precisar fechar o cursor, mas importante que se torne um hbito fech-lo, pois assim,
estaremos poupando recursos da mquina.
38O9T'4T&+ Existe um limite mximo de cursores abertos por usurio que determinado pelo
parmetro do Oracle OPEN_CURSORS.
Exemplo:
DECLARE
-- Declarao de variveis
vCodigoRep representante.rep_in_codigo;tQ,;
vNomeRep representante.rep_st_nome;tQ,;
-- Declarao de cursores
CURSOR cs_representante is
SELECT rep_in_codigo= rep_st_nome
FROM representante;
BEGIN
-- Abre cursor
OPEN cs_representante;

-- Executa um loop com 10 ciclos
FOR i IN 5..56 LOOP
-- Extrai dados o registro corrente do cursor e avana para o prximo
FETC) cs_representante INTO vCodigoRep = vNomeRep;
-- Imprime dados extrados na tela
dbms_output.put_line(vCodigoRep##' G '##vNomeRep);
Elaborado por Josinei Barbosa da Silva
Pgina 51 de 154
Treinamento
END LOOP;
-- Fechar cursor
CLOSE cs_representante;
END;
1
'tributos de cursores ex%l=citos
A PL/SQL disponibiliza quatro atributos de cursor que so muito teis, como mostrado na tabela a
seguir:
Tabela 5: Atributos de cursor e1#lCcito
Excees Descrio
%rowcount Mostra o nmero de linhas do cursor
%found Retorna TRUE se o mais recente FETCH retornar uma linha.
%notfound Retorna TRUE se o mais recente FETCH no retornar uma linha.
%isopen Retorna TRUE se o cursor estiver aberto.
Um atributo de cursor explcito no pode ser referenciado diretamente de uma instruo SQL.
necessrio atribuir o seu retorno para uma varivel.
Para extrair o valor de um atributo, ele deve ser precedido do nome do cursor, como no exemplo
abaixo:
DECLARE
-- Declarao de variveis
vCodigoRep representante.rep_in_codigo;tQ,;
vNomeRep representante.rep_st_nome;tQ,;
-- Declarao de cursores
CURSOR cs_representante is
SELECT rep_in_codigo= rep_st_nome
FROM representante;
BEGIN
-- Abre cursor se ainda no estiver aberto
IF NOT cs_representante;ISOPEN T)EN
OPEN cs_representante;
END IF;

-- Executa um loop com 10 ciclos
FOR i IN 5..56 LOOP
-- Extrai dados o registro corrente do cursor e avana para o prximo
FETC) cs_representante INTO vCodigoRep = vNomeRep;
-- Imprime dados extrados na tela
dbms_output.put_line(vCodigoRep##' G '##vNomeRep);
END LOOP;
-- Fechar cursor
CLOSE cs_representante;
END;
1
No exemplo acima, verificamos se o cursor j estava aberto, usando para isso o atributo %ISOPEN.
Alm de utilizar o atributo %ISOPEN para verificar se o cursor est aberto, recomendado o uso do
atributo %NOTFOUND para determinar quando sair do LOOP. Este atributo retorna FALSE se o ltimo FETCH
Elaborado por Josinei Barbosa da Silva
Pgina 52 de 154
Treinamento
retornar uma linha e TRUE se no retornar (Antes do primeiro FETCH, o valor do atributo NULL).
Abaixo, temos um exemplo do uso do atributo %NOTFOUND:
DECLARE
-- Declarao de variveis
vCodigoRep representante.rep_in_codigo;tQ,;
vNomeRep representante.rep_st_nome;tQ,;
-- Declarao de cursores
CURSOR cs_representante is
SELECT rep_in_codigo= rep_st_nome
FROM representante;
BEGIN
-- Abre cursor se ainda no estiver aberto
IF NOT cs_representante;ISOPEN T)EN
OPEN cs_representante;
END IF;

-- Executa um loop com 10 ciclos
LOOP
-- Extrai dados o registro corrente do cursor e avana para o prximo
FETC) cs_representante INTO vCodigoRep = vNomeRep;
-- Sai do Loop quando no houver mais registros para processar
EXIT ()EN cs_representante;NOTFOUND;
-- Imprime dados extrados na tela
dbms_output.put_line(vCodigoRep##' G '##vNomeRep);
END LOOP;
-- Fechar cursor
CLOSE cs_representante;
END;
1
O atributo %ROWCOUNT tambm pode ser de grande utilidade, mas lembre-se que:
Antes do primeiro FETCH (mesmo com o cursor j aberto), seu valor 0 (zero);
Se o cursor no estiver aberto e for referenciado pelo atributo %ROWCOUNT, ocorrer a
exceo INVALID_CURSOR .
Para processar diversas linhas de um cursor explcito, definimos um LOOP para realizar um fetch em
cada iterao. Eventualmente, todas as linhas do cursor so processadas e quando um FETCH no retornar
mais linhas, o atributo %NOTFOUND passa a ser TRUE. Use os atributos de cursores explcitos para testar o
sucesso de cada FETCH, antes de fazer qualquer referncia no cursor. Um LOOP infinito ir acontecer se
nenhum critrio de sada for utilizado.
Cursores2 re*istros e o atributo C9O-TD&
Ao invs de declararmos uma varivel para cada coluna retornada por um cursor, podemos definir
registros com a mesma estrutura do cursor e declarar variveis do tipo desses registros.
O mais prtico definir essa varivel de registro utilizando o atributo %ROWTYPE, mas claro que
Elaborado por Josinei Barbosa da Silva
Pgina 53 de 154
Treinamento
cada caso deve ser estudado com cautela.
conveniente utilizar o atributo %ROWTYPE, pois as linhas do cursor sero carregadas neste
registro e seus valores carregados diretamente nos campos deste registro.
A seguir temos dois exemplos. No primeiro mostramos a utilizao do Tipo registro e no segundo, o
uso do atributo %ROWTYPE:
Exemplo do uso de Tipo Registro
DECLARE
-- Declarao de tipo registro
T<PE TRepresentante IS RECORD(
Codigo representante.rep_in_codigo;tQ,=
Nome representante.rep_st_nome;tQ,
);
-- Declarao de variveis
rRep Trepresentante;
-- Declarao de cursores
CURSOR cs_representante is
SELECT rep_in_codigo= rep_st_nome
FROM representante;
BEGIN
-- Abre cursor se ainda no estiver aberto
IF NOT cs_representante;ISOPEN T)EN
OPEN cs_representante;
END IF;

-- Executa um loop com 10 ciclos
LOOP
-- Extrai dados o registro corrente do cursor e avana para o prximo
FETC) cs_representante INTO rRep;
-- Sai do Loop quando no houver mais registros para processar
EXIT ()EN cs_representante;NOTFOUND;
-- Imprime dados extrados na tela
dbms_output.put_line(rRep.Codigo##' G '##rRep.Nome);
END LOOP;
-- Fechar cursor
CLOSE cs_representante;
END;
1
No exemplo acima, embora seja melhor do que definir uma varivel para cada coluna do cursor, se a
SELECT do cursor for alterada para retornar mais colunas ou menos, o TYPE TRepresentante tambm
precisar ser alterado.
Por esse motivo, o uso do atributo %ROWTYPE muito mais simples, como podemos ver no segundo
exemplo:
DECLARE
-- Declarao de cursores
CURSOR cs_representante is
SELECT rep_in_codigo= rep_st_nome
FROM representante;
Elaborado por Josinei Barbosa da Silva
Pgina 54 de 154
Treinamento
-- Declarao de variveis
rRep cs_representante;RO(T<PE;
BEGIN
-- Abre cursor se ainda no estiver aberto
IF NOT cs_representante;ISOPEN T)EN
OPEN cs_representante;
END IF;

-- Executa um loop com 10 ciclos
LOOP
-- Extrai dados o registro corrente do cursor e avana para o prximo
FETC) cs_representante INTO rRep;
-- Sai do Loop quando no houver mais registros para processar
EXIT ()EN cs_representante;NOTFOUND;
-- Imprime dados extrados na tela
dbms_output.put_line(rRep.rep_in_codigo##' G '##rRep.rep_st_nome);
END LOOP;
-- Fechar cursor
CLOSE cs_representante;
END;
1
Dessa maneira, qualquer alterao na estrutura do cursor, refletir na varivel que rRep.
Cursores ex%l=citos automati>ados 6!OO Cursor (O97
Os LOOPs Cursor FOR so ideais quando voc quer fazer o LOOP em todos os registros retornados
pelo cursor. Com o LOOP Cursor FOR voc no deve declarar o registro que controla o LOOP. Da mesma
forma, voc no deve usar o LOOP Cursor FOR quando as operaes do cursor precisarem ser tratadas
manualmente.
Um LOOP Cursor FOR faz as seguintes coisas implicitamente:
1. Declara o ndice do LOOP;
2. Abre o cursor;
3. Faz o FETCH da linha seguinte a partir do cursor para cada iterao do LOOP;
4. Fecha o cursor quando todas as linhas so processadas ou quando o LOOP encerrado.
Um LOOP Cursor FOR processa linhas em um cursor explcito. sto uma facilidade por que o
cursor aberto, linhas so carregadas em cada iterao do LOOP, o LOOP finalizado quando a ltima linha
processada e o cursor fechado, automaticamente.
Sintaxe:
FOR nome_registro IN nome_cursor LOOP
Comando1;
Comando2;
Comando3;
END LOOP;
Onde:
Elaborado por Josinei Barbosa da Silva
Pgina 55 de 154
Treinamento
nome_registro o nome do registro implcito declarado;
nome_cursor o nome do cursor previamente declarado pelo usurio.
Exemplo:
DECLARE
-- Declarao de cursores
CURSOR cs_representante is
SELECT rep_in_codigo= rep_st_nome
FROM representante;
BEGIN
-- Inicia o loop no conjunto ativo do cursor
FOR rRep in cs_Representante LOOP
-- Imprime dados extrados na tela
dbms_output.put_line(rRep.rep_in_codigo##' G '##rRep.rep_st_nome);
END LOOP;
END;
1
38O9T'4T&+
No declare o registro que controla o LOOP por que ele declarado implicitamente;
No use LOOP Cursor FOR quando as operaes do cursor tiverem que ser manipuladas
explicitamente.
L$$P ursor 6$" usando Sub-onsultas
Quando usamos uma Sub-consulta em um LOOP Cursor FOR, no precisamos declarar um cursor.
Veja abaixo:
BEGIN
-- Inicia o loop no conjunto ativo do cursor
FOR rRep in (SELECT rep_in_codigo= rep_st_nome
FROM representante)
LOOP
-- Imprime dados extrados na tela
dbms_output.put_line(rRep.rep_in_codigo##' G '##rRep.rep_st_nome);
END LOOP;
END;
1
Embora parea simples, o uso desse recurso pode no ser favorvel a formatao do cdigo e,
conseqentemente, sua manuteno.
No exemplo acima, montamos um LOOP Cursor FOR com base em uma declarao SELECT que
acessa apenas uma tabela e retorna duas colunas.
Agora, imagine se fosse uma SQL com relacionamento entre vrias tabelas, com chaves primrias
compostas, retornando dezenas de colunas, etc., etc.. Com certeza o cdigo no estaria assim, to
amigvel.
Por isso, procure declarar os cursores, SEMPRE, considerando ser uma boa %rtica de
%ro*ramao.
Elaborado por Josinei Barbosa da Silva
Pgina 56 de 154
Treinamento
assando %ar0metros %ara cursores
Podemos passar parmetros para um cursor previamente declarado. sto significa que podemos abrir
e fechar um cursor dentro de um bloco PL/SQL, retornando diferentes linhas no cursor em cada ocasio.
Para cada execuo, o cursor anterior fechado e reaberto com um novo conjunto de parmetros.
Esse recurso pode ser utilizado em cursores manipulados manualmente ou em cursores
automatizados.
Sintaxe:
CURSOR nome_cursor [(nome_parametro tipo= ...)] IS
Comando S/ct;
Onde:
nome_cursor o nome do cursor declarado pelo usurio.
nome_parametro o nome do parmetro.
tipo o tipo do parmetro.
Para cada parmetro declarado no cursor, dever existir um correspondente quando o comando
OPEN for usado. Estes parmetros, possuem os mesmos tipos de dados que as variveis escalares, a nica
diferena que no fornecemos seus tamanhos.
Exemplo:
DECLARE
-- Declarao de cursores
CURSOR cs_representante(pMenorMedia NUMBER= pMaiorMedia NUMBER) is
SELECT rep_in_codigo= rep_st_nome
FROM representante
()ERE rep_vl_mediamensalvendas BET(EEN pMenorMedia AND pMaiorMedia;
BEGIN
-- Abre cursor para representantes com mdia entre 40000 e 80000
dbms_output.put_line('R,&snt$nts co! !O%i$ nt& 96666 R6666');
FOR rRep in cs_Representante(96666=R6666) LOOP
/* Imprime na tela os vendedores cuja mdia de vendas mensal
Est no intervalo de 40000 e 80000
*/
dbms_output.put_line(rRep.rep_in_codigo##' G '##rRep.rep_st_nome);
END LOOP;
-- Abre cursor para representantes com mdia entre 80001 e 100000
dbms_output.put_line('R,&snt$nts co! !O%i$ nt& R6665 566666');
FOR rRep in cs_Representante(R6665=566666) LOOP
/* Imprime na tela os vendedores cuja mdia de vendas mensal
Est no intervalo de 80001 e 100000
*/
dbms_output.put_line(rRep.rep_in_codigo##' G '##rRep.rep_st_nome);
END LOOP;
END;
1
Cursores im%l=citos
Elaborado por Josinei Barbosa da Silva
Pgina 57 de 154
Treinamento
Como j mencionado antes, o Oracle cria e abre um cursor para cada declarao SQL que no faz
parte de um cursor declarado explicitamente. O cursor implcito mais recente pode ser chamado de cursor
SQL. Voc no pode usar os comandos OPEN, CLOSE e FETCH com um cursor implcito. Entretanto, voc
pode usar os atributos de cursor para acessar as informaes sobre a declarao SQL executada mais
recentemente por meio do cursor SQL
'tributos do cursor im%l=cito
Assim como os cursores explcitos, os cursores implcitos tambm usam os atributos. Os atributos do
cursor implcito so os mesmo do cursor explcito, ou seja:
Tabela 6: Atributos de cursor im#lCcito
Excees Descrio
%rowcount Mostra o nmero de linhas do cursor
%found Retorna TRUE se o mais recente FETCH retornar uma linha.
%notfound Retorna TRUE se o mais recente FETCH no retornar uma linha.
%isopen Retorna TRUE se o cursor estiver aberto.
Como os cursores implcitos no tm nome, voc deve substituir o nome do cursor pela palavra SQL
junto ao atributo.
O cursor implcito contm as informaes sobre o processamento da ltima declarao SQL
(NSERT, UPDATE, DELETE e SELECT NTO) que no foi associada a um cursor explcito.
Voc pode usar os atributos do cursor implcito nas declaraes PL/SQL, no nas declaraes SQL.
Quando estudamos as excees, vimos o uso de um atributo de cursor implcito em um de nossos
exemplos, mas vamos relembr-lo agora que entendemos o seu funcionamento por completo:
DECLARE
-- Declarar exceo
eRegistroInexistente xc,tion;
BEGIN
-- Excluir cliente
DELETE cliente c
()ERE c.cli_in_codigo 3 5666;

-- Se cliente no existe, gerar exceo
IF SIL;NOTFOUND T)EN
&$is eRegistroInexistente;
END IF;

-- Validar excluso
COMMIT;
EXCEPTION
-- Se registro no existe, informa usurio
()EN eRegistroInexistente T)EN
ROLLBACH;
dbms_output.put_line('C/int 5666 n*o xistJ'##chr(56)##
'NnKu! &List&o +oi xc/u-%o''
);

END;
1
Nesse exemplo utilizamos o atributo %NOTFOUND para verificarmos quantos registros foram afetados
Elaborado por Josinei Barbosa da Silva
Pgina 58 de 154
Treinamento
pela comando DELETE. Note que o atributo precedido pela palavra SQL, que refere-se a instruo SQL
mais recente (o DELETE)
:ariveis de cursor
Como vimos nos tpicos anteriores, o cursor da PL/SQL uma rea nomeada do banco de dados.
Uma varivel de cursor, por definio, uma referncia quela rea nomeada. Uma varivel de cursor
como um ponteiro de uma linguagem de programao como C. As variveis de cursor indicam a rea de
trabalho de uma consulta, na qual o conjunto de resultados da consulta armazenado. Uma varivel de
cursor tambm dinmica por natureza porque ela no est vinculada a uma consulta especfica. A Oracle
conserva essa rea de trabalho enquanto um ponteiro de cursor est apontando para ela. Voc pode usar
uma varivel de cursor para qualquer consulta de tipo compatvel.
Um dos recursos mais significativos da varivel de cursor que a Oracle permite passar uma varivel
de cursor como um argumento para um procedimento ou uma chamada de funo.
Para criar uma varivel de cursor, voc primeiro deve criar um tipo de cursor referenciado e depois
declara uma varivel de cursor daquele tipo.
A sintaxe para definir um tipo cursor a seguinte:
T<PE cursor_type_name IS REF CURSOR RETURN return_type;
Nessa sintaxe, REF quer dizer referncia, cursor_type_name o nome do tipo do cursor e
return_type a especificao de dados do tipo de cursor de retorno. A clusula RETURN opcional.
As variveis de cursores podem ser muito teis em situao em que o ambiente aplicativo executa
um procedimento PL/SQL (Como procedure ou function) e passa como argumento um cursor e/ou recebe
um argumento do tipo cursor.
Vejamos um exemplo simples de como podemos utilizar uma varivel do tipo cursor:
DECLARE
-- Declarao de tipos
T<PE TCursor IS REF CURSOR;
-- Declarao de variveis
vCursor TCursor;
-- Sub-rotinas
PROCEDURE GerarXML(pCursor TCursor) IS
BEGIN
NULL;
/* implementao para gerar o XML a partir do cursor*/
END;
BEGIN
-- Abre o cursor atribuindo para a varivel criada
OPEN vCursor FOR
SELECT rep_in_codigo= rep_st_nome
FROM representante;
-- Executa a rotina que recebe o cursor e converte seu contedo para XML
GerarXML(vCursor);
-- Fecha o cursor
CLOSE vCursor;
END;
1
Elaborado por Josinei Barbosa da Silva
Pgina 59 de 154
Treinamento
38O9T'4T&+ Nas prximas lies, estudaremos os procedimentos armazenados, ou seja,
procedures e functions, mas como voc pode observar no exemplo acima, podemos criar funes e
procedures dentro de qualquer bloco PL/SQL. So as chamadas subrotinas.
Elaborado por Josinei Barbosa da Silva
Pgina 60 de 154
Treinamento
Blocos an?nimos2 %rocedimentos e 1unes
Em PL/SQL temos trs tipos de blocos: Blocos Annimos, procedimentos (Procedures) e funes (
6unctions7.
Um bloco annimo, como j vimos (e como o prprio nome sugere), no nomeado e esta a nica
diferena entre ele e os procedimentos e funes.
Um procedimento um conjunto de declaraes SQL e PL/SQL (como um bloco annimo) agrupadas
logicamente, que executam uma tarefa especfica. Ele um programa em miniatura.
Abaixo temos uma figura que mostra a diferena de nomeao entre bloco annimo, procedure e
function:
6igura 89( Bloco an:nimo; procedure e function
Procedimentos e funes podem ser implementadas de trs formas:
1. Como subrotina de um bloco annimo;
2. Como procedimento independente e armazenado no banco de dados;
3. Como uma rotina de um pacote armazenado no banco de dados.
4esta lio 1ocaremos as subrotinas em blocos an?nimos@
A principal vantagem de usar subrotinas que voc pode modularizar seu programa, centralizando
cdigos que seriam repetidos. Desta forma, caso tal cdigo precisasse de manuteno, estaria
implementado em apenas um ponto do programa.
Abaixo segue um exemplo simples, onde uma procedure e uma funo so subrotinas de bloco
annimo.
DECLARE
-- Declarao de cursores
CURSOR cs_cliente is
SELECT c.*
FROM cliente c;

Elaborado por Josinei Barbosa da Silva
Pgina 61 de 154
Treinamento
-- declarao de variveis
rCli cs_cliente;RO(T<PE;

/***********************************************************/
/* Subrotinas */
/***********************************************************/

-- funo definir se o cliente compra o seu potencial mximo
FUNCTION ClienteCompraPotencial(pMediaCompraMensal NUMBER=
pMaiorCompra NUMBER)
RETURN BOOLEAN IS
-- Declarao de variveis
vPercentualPotencial NUMBER;
vResult BOOLEAN;
BEGIN
-- Definir quanto a mdia mensal de compras representa da maior compra
vPercentualPotencial :3 (pMediaCompraMensal 1 pMaiorCompra) * 566;

-- Resultado s verdadeiro se percentual for igual ou mairo a 80
vResult :3 vPercentualPotencial A3 R6;

RETURN(vREsult);
END;
-- procedimento para exibir mensagem na tela
PROCEDURE Exibir(pMensagem 7ARC)AR4) IS
BEGIN
dbms_output.put_line(pMensagem);
END;
BEGIN
-- Inicia o loop no conjunto ativo do cursor
FOR rCli in cs_Cliente LOOP
IF ClienteCompraPotencial(rCli.cli_vl_mediacomprasmensal=
rCli.cli_vl_maiorcompra
) T)EN
-- Imprime dados extrados na tela
Exibir(rCli.cli_in_codigo##' G '##rCli.cli_st_nome);
END IF;
END LOOP;
END;
1
No exemplo acima utilizamos a funo ClienteCompraPotencial para verificar se o cliente est
comprando aquilo que tem potencial (em valor) e o procedimento Exibir para imprimir uma mensagem na
tela.
magine que nosso bloco annimo fosse muito maior e que, em vrios pontos precisasse verificar se
o Cliente compra o potencial que tem! Ao invs de reescrever a regra para esse clculo em todos esses
pontos, basta chamar a funo que passaria a ser o nico ponto de manuteno dessa regra.
Outra coisa que deve ser observada que os procedimentos e funes recebem parmetros, como
vimos nos cursores .
38O9T'4T&+ Ao invocar um procedimento que possui parmetros de entrada, procure informar o
Elaborado por Josinei Barbosa da Silva
Pgina 62 de 154
Treinamento
valor de cada parmetro na ordem definida, pois no h como o Oracle "adivinhar os valores se estiverem
"embaralhados. possvel informar parmetros em ordem diferente da definida, porm mais trabalhoso,
pois voc precisar informar alm do valor, o nome do parmetro ao que se refere, como abaixo:
DECLARE
'''
BEGIN
'''
ClienteCompraPotencial(pMaiorCompra =>100000, pMediaCompraMensal => 80000)
END;
1
Elaborado por Josinei Barbosa da Silva
Pgina 63 de 154
Treinamento
&xerc=cios %ro%ostos
1. Crie um bloco annimo com um cursor automatizado que carregue todos os clientes de um
determinado ramo de atividade, mediante o uso de parmetros. Abra um loop para cada ramo de
atividade possvel (HPERMERCADO, SUPERMERCADO, MERCADO, MERCEARA). Antes de
iniciar cada loop, imprima na tela o ramo de atividade. Durante o loop, se a data da maior compra
do cliente for do ms corrente, imprima na tela a seguinte mensagem
<* maior compra do cliente 0 foi reali=ada no m>s corrente<.
Onde 0 cdigo do cliente.
2. Crie um bloco annimo com um cursor no automatizado que carregue todos os produtos e imprima
na tela:
<&ovo record de venda do produto 0 ? n<, se o valor da ltima venda for maior ou igual ao valor
da maior venda, onde 0 o cdigo do produto e n o valor da maior venda;
<$ record de venda do produto 0 n4o foi alcan1ado<, se o valor da ltima venda no for maior ou
igual ao valor da maior venda;
3. Crie um bloco annimo com um cursor que carregue todos os representantes e imprima na tela:
<* meta mensal de vendas do representante 0 est) abai0o de sua m?dia mensal< , se a meta
mensal de vendas do representante for menor do que sua mdia mensal;
<$ representante 0 tem potencial maior do @ue sua meta mensal.<, se a meta mensal for maior
do que sua mdia e menor do que sua maior venda;
<$ representante 0 atingiu todo o potencial de vendasA<, se a meta mensal for maior do que sua
mdia e maior do que sua maior venda, ou seja, se no atender as duas primeiras condies.
(utilize IF...ELSIF...ELSE);
onde 0 o cdigo do represente.
4. Construa um bloco annimo que:
a) nicialize uma varivel do tipo DATE com a data do primeiro dia do ms atual;
b) Execute um loop que simule um REPEAT...UNTL;
c) ncremente a varivel em um dia, a cada ciclo do loop e imprima na tela o valor obtido;
d) Abandone o loop quando a data no avanar o ms atual.
5. Reproduza o exerccio anterior declarando no bloco annimo, uma funo para incrementar a data a
ser impressa na tela.
Elaborado por Josinei Barbosa da Silva
Pgina 64 de 154
Treinamento
03 Stored Procedures
1. rocedimentos arma>enados;
2. acotes;
Elaborado por Josinei Barbosa da Silva
Pgina 65 de 154
Treinamento
rocedimentos arma>enados 6Stored Procedure7
Um procedimento armazenado um procedimento que foi compilado e armazenado dentro do banco
de dados. Depois de armazenado o procedimento um objeto de esquema, ou seja, um objeto especfico
do banco de dados.
or que usar os %rocedimentos$
Os procedimentos so criados para solucionar um problema ou tarefa especficos. Os procedimentos
PL/SQL oferecem as seguintes vantagens:
Na PL/SQL voc pode personalizar um procedimento segundo seus requisitos especficos;
Os procedimentos so modulares, o que significa que eles deixam voc dividir um programa
em unidades gerenciveis e bem definidas;
Como os procedimentos so armazenados em um banco de dados, eles podem ser
reutilizados. Aps um procedimento ter sido validado, ele pode ser usado repetidamente sem ser
recompilado ou distribudo pela rede;
Os procedimentos aumentam a segurana do banco de dados. Voc pode restringir o acesso
ao banco de dados permitindo que os usurios acessem os dados apenas por meio dos
procedimentos armazenados;
Os procedimentos aproveitam os recursos de memria compartilhados.
Uma das coisas mais teis que voc pode fazer com o seu conhecimento da PL/SQL talvez seja us-
la para escrever stored procedures. A encapsulao do cdigo que voc escreveu anteriormente em uma
funo armazenada permite que voc a compile uma vez e armazene no banco de dados para uso futuro.
Da prxima vez que quiser executar aquele bloco PL/SQL (toda stored procedure um bloco PL/SQL), voc
s precisa invocar a funo.
As duas maiores diferenas entre um bloco annimo e uma stored procedure so:
1. Blocos annimos no so armazenados no banco de dados e stored procedures so. Quando
o desenvolvedor cria um bloco annimo, responsabilidade dele guarda o seu cdigo, ou seja, se o
bloco for desenvolvido, executado e no for salvo em algum arquivo de sistema operacional (ou
objeto de banco de dados criado para isso), seu cdigo ser perdido. Uma stored procedure, para
ser usada, deve ser compilada e quando compilada o Oracle armazena seu cdigo no dicionrio
de dados, permitindo assim a sua recuperao para posterior manuteno;
2. Stored Procedure possui um cabealho para nome-lo, declarar variveis de input e output e
tipo de retorno; blocos annimos no.
A estrutura de um procedimento armazenada exatamente igual procedimentos implementados
dentro de blocos PL/SQL e o que define que ele ser armazenada o uso do comando CREATE OR
REPLACE antes de seu nome.
rocedimentos versus 1unes
Os procedimentos e as funes so sub programas PL/SQL que so armazenados no banco de
dados. A diferena significativa entre os dois so apenas os tipos de sada dos dois objetos gerados. Uma
funo retorna um valor simples, enquanto que um procedimento usado para executar processamento
complicado quando voc quer ter de volta uma quantidade substancial de informaes.
A seguir veremos um pouco mais sobre procedimentos e funes.
Elaborado por Josinei Barbosa da Silva
Pgina 66 de 154
Treinamento
rocedimentos
Uma procedure nada mais do que um bloco PL/SQL nomeado sem retorno definido (somente
funes possuem um retorno definido). A grande vantagem sobre um bloco PL/SQL annimo que pode
ser compilado e armazenado no banco de dados como um objeto de schema. Graas a essa caracterstica,
as procedures so de fcil manuteno, o cdigo reutilizvel e permitem que trabalhemos com mdulos
de programa.
A sintaxe bsica de uma procedure :
CREATE [OR REPLACE] PROCEDURE [scK!$.]nome_da_procedure
[(parmetro1 [modo1] tipodedado1=
parmetro2 [modo2] tipodedado2=
...)]
IS|AS
[Bloco PL1SIL]
Onde:
replace indica que caso a procedure exista ela ser eliminada e substituda pela nova verso
criada pelo comando;
nome_da_procedure indica o nome da procedure;
parBmetro indica o nome da varivel PL/SQL que passada na chamada da procedure ou o
nome da varivel que retornar os valores da procedure ou ambos. O que ir conter em parmetro
depende de modoC
modo ndica que o parmetro de entrada (IN), sada (OUT) ou ambos (IN OUT).
importante notar que IN o modo default, ou seja, se no dissermos nada o modo do nosso
parmetro ser, automaticamente, IN;
tipodedado& indica o tipo de dado do parmetro. Pode ser qualquer tipo de dado do SQL ou
do PL/SQL. Pode usar referencias como %TYPE, %ROWTYPE ou qualquer tipo de dado escalar ou
composto. Tambm possvel definir um valor default. Ateno: no possvel fazer qualquer
restrio ao tamanho do tipo de dado neste ponto.
isDas( Por conveno usamos IS na criao de procedures armazenadas e AS quando
estivermos criando pacotes.
Bloco PLDSQL inicia com uma clusula BEGIN e termina com END ou END
nome_da_procedure onde as aes sero executadas.
Abaixo, temos um exemplo de uma procedure armazenada que recupera a maior vendas de um
produto dentro de um perodo e atualiza essa informao no cadastro do produto, se for maior do que o
valor j existente :
CREATE OR REPLACE PROCEDURE pr_AtualizaMaiorVendaProduto(pProduto
produto.pro_in_codigo;T<PE=
pDataInicial DATE=
pDataFinal DATE
) IS
vValorMaiorVenda_old NUMBER :3 6;
vValorMaiorVenda_new NUMBER :3 6;
BEGIN
-- Recupera o valor da maior venda registrada no produto
BEGIN
SELECT pro_vl_maiorvenda
Elaborado por Josinei Barbosa da Silva
Pgina 67 de 154
Treinamento
INTO vValorMaiorVenda_old
FROM produto
()ERE pro_in_codigo 3 pProduto;
EXCEPTION
()EN no_data_found T)EN
raise_application_error(G46566='P&o%uto n*o c$%$st&$%oJ');
()EN OT)ERS T)EN
raise_application_error(G46566='E&&o $o tnt$& &cu,&$& %$%os %o
,&o%utoJ');
END;
-- Recupera o valor da maior venda do produto no perodo
SELECT MAX(i.infv_vl_total)
INTO vValorMaiorVenda_new
FROM nota_fiscal_venda n=
item_nota_fiscal_venda i
()ERE n.nfv_dt_emissao BET(EEN pDataInicial AND pDataFinal
AND n.nfv_in_numero 3 i.nfv_in_numero
AND i.pro_in_codigo 3 pProduto;
-- Se a maior venda do perodo for maior do que a j cadastrada, atualiza
IF vValorMaiorVenda_new A vValorMaiorVenda_old T)EN
UPDATE produto
SET pro_vl_maiorvenda 3 vValorMaiorVenda_new
()ERE pro_in_codigo 3 pProduto;
END IF;
END;
1
Esse procedimento armazenado pode ser executado diretamente do prompt de uma ferramenta de
comandos, como abaixo:
SILA xcut pr_AtualizaMaiorVendaProduto(46=S<SDATE=last_day(S<SDATE));
Ou a partir de um bloco PL/SQL, como abaixo:
BEGIN
pr_AtualizaMaiorVendaProduto(46=S<SDATE=last_day(S<SDATE));
END;
1
(unes
Como vimos antes, as funes so semelhantes aos procedimentos da PL/SQL, exceto pelas
seguintes diferenas:
As funes retornam um valor;
As funes so usadas como parte de uma expresso.
A sintaxe bsica de uma fun!o :
CREATE [OR REPLACE] FUNCTION [scK!$.]nome_da_funcao
[(parmetro1 [modo1] tipodedado1=
Elaborado por Josinei Barbosa da Silva
Pgina 68 de 154
Treinamento
parmetro2 [modo2] tipodedado2=
...)]
RETURN TipoRetorno IS|AS
[Bloco PL1SIL]
Onde:
replace indica que caso a funo exista ela ser eliminada e substituda pela nova verso
criada pelo comando;
nome_da_funcao indica o nome da funo;
parBmetro indica o nome da varivel PL/SQL que passada na chamada da funo ou o
nome da varivel que retornar os valores da funo ou ambos. O que ir conter em parmetro
depende de modoC
modo ndica que o parmetro de entrada (IN), sada (OUT) ou ambos (IN OUT).
importante notar que IN o modo default, ou seja, se no dissermos nada o modo do nosso
parmetro ser, automaticamente, IN;
tipodedado& indica o tipo de dado do parmetro. Pode ser qualquer tipo de dado do SQL ou
do PL/SQL. Pode usar referencias como %TYPE, %ROWTYPE ou qualquer tipo de dado escalar ou
composto. Tambm possvel definir um valor default. Ateno: no possvel fazer qualquer
restrio ao tamanho do tipo de dado neste ponto.
isDas( Por conveno usamos IS na criao de funes armazenadas e AS quando
estivermos criando pacotes.
Bloco PLDSQL inicia com uma clusula BEGIN e termina com END ou END nome_da_funcao
onde as aes sero executadas.
Vamos escrever uma funo armazenada que recebe o cdigo do cliente e retorna a quantidade de
notas faturadas para ele, dentro de um perodo:
CREATE OR REPLACE FUNCTION fn_QtdeNFV(pCodigoCliente NUMBER=
pDataInicial DATE=
pDataFinal DATE
)
RETURN INTEGER IS
-- Declarao de variveis
vResult INTEGER 236;
BEGIN
-- Recupera quantidade de notas do cliente dentro do perodo
SELECT COUNT(*)
INTO vResult
FROM nota_fiscal_venda nfv
()ERE nfv.cli_in_codigo 3 pCodigoCliente
AND nfv.nfv_dt_emissao BET(EEN pDataInicial AND pDataFinal;
RETURN(vResult);
END;
1
Como a funo acima armazenada, podemos us-la das seguintes formas:
1. Atribuindo seu retorno diretamente para uma varivel:
DECLARE
Elaborado por Josinei Barbosa da Silva
Pgina 69 de 154
Treinamento
vQtdeNFV NUMBER;
BEGIN
vQtdeNFV :3 fn_QtdeNFV(56= ADD_MONTHS(S<SDATE=G5)= S<SDATE);
END;
1
Nesse exemplo, atribumos para a varivel vQtde&6#, o retorno da funo fQtde&6#,
tendo como perodo os ltimos trinta dias (SYSDATE igual a data atual e ADD_MONTHS
adiciona o nmero de meses informados)
2. Recuperando seu valor atravs de uma instruo SQL:
SELECT fn_QtdeNFV(56= ADD_MONTHS(S<SDATE=G5)= S<SDATE) QtdeNFV
FROM dual;
A tabela DUAL uma tabela especial do Oracle que sempre existe, sempre tem
exatamente uma linha e sempre tem exatamente uma coluna. Ela a tabela perfeita para ser
usada quando se experimentam as funes. A seleo da funo na tabela DUAL faz com que
o resultado da funo seja exibido.
Se a declarao SELECT acima estivesse dentro de um bloco PL/SQL, o retorno da
funo fQtde&6# poderia ser atribudo uma varivel atravs da clusula INTO.
38O9T'4T&+ Evite utilizar comandos DML (INSERT, UPDATE e DELETE) em funes, pois se elas
forem invocadas de uma declarao SELECT voc receber o seguinte erro:
ORAG59::5: no possvel executar uma operao DML dentro de uma consulta
8anuteno de %rocedimentos arma>enados
Para recompilar explicitamente um procedimento armazenado, use o comando ALTER
PROCEDURE, como no exemplo abaixo.
ALTER PROCEDURE pr_AtualizaMaiorVendaProduto COMPILE;
(ou ALTER FUNCTION para funes armazenadas)
O comando acima, como pode ser observado, no altera a declarao ou definio do procedimento.
Para isso, voc deve usar o comando CREATE OR REPLACE [PROCEDURE | FUNCTION], junto com todo
o cdigo do procedimento (da mesma forma que feito quando se cria o procedimento).
Para excluir um procedimento armazenado, voc deve utilizar o comando DROP [PROCEDURE |
FUNCTION]. A sintaxe do comando :
DROP PROCEDURE | FUNCTION nome_do_procedimento;
Onde:
DROP o comando de excluso;
PROCEDURE ou FUNCTION defini o tipo de procedimento a ser excludo;
nome_do_procedimento o nome do procedimento armazenado.
5sando os %ar0metros
Os procedimentos usam os parBmetros para passar as informaes. Quando um parmetro est
sendo passado para um procedimento, ele conhecido como um parBmetro real. Os parmetros declarados
Elaborado por Josinei Barbosa da Silva
Pgina 70 de 154
Treinamento
como internos a um procedimento so conhecidos como parmetros internos ou formais.
O parmetro real e seu parmetro formal correspondente devem pertencer a datatypes compatveis.
Por exemplo, a PL/SQL no pode converter um parmetro real com um datatype DATE para um parmetro
formal com um datatype LONG. Nesse caso, o Oracle retornaria uma mensagem de erro. Essa questo de
compatibilidade tambm se aplica aos valores de retorno.
De1inies de %ar0metro
Quando voc invoca um procedimento, deve passar um valor para cada um dos parmetros do
procedimento. Se voc passar valores para o parmetro, eles so posicionais e devem aparecer na mesma
ordem em que aparecem na declarao do procedimento. Se voc passar nomes de argumentos, eles
podem aparecer em qualquer ordem. Voc pode ter uma combinao entre valores e nomes nos valores do
argumento. Quando esse for o caso, os valores identificados na ordem devem preceder os nomes de
argumento.
De%endEncias de %rocedimentos
Um dos recursos inerentes da PL/SQL que ela verifica o banco com base nos objetos de dados
para ter certeza de que as operaes de um procedimento, uma funo ou um pacote so possveis, os
quais o usurio tem acesso. Por exemplo, se voc tiver um procedimento que exija acesso a diversas
tabelas e vises, a PL/SQL verifica durante o tempo de compilao se aquelas tabelas e vises esto
presentes e disponveis para o usurio. Diz-se que o procedimento ? dependente dessas tabelas e vises.
38O9T'4T&+ A PL/SQL recompila automaticamente todos os objetos dependentes quando voc
recompila explicitamente o objeto pai. Essa recompilao automtica dos objetos dependentes acontece
quando o objeto dependente chamado. Assim sendo, no recomendado recompilar um mdulo pai de
um sistema de produo, pois isso faz com que todos os objetos dependentes sejam recompilados e,
conseqentemente, pode causar problemas de desempenho para o seu sistema de produo.
Se*urana da invocao de %rocedimento
Ao executar um procedimento armazenado, o Oracle verifica se voc tem os privilgios necessrios
para tanto. Se voc no tiver as permisses de execuo apropriadas, ocorre um erro.
Uma vez tendo direito de execuo de um procedimento, ao execut-lo, o Oracle no verifica se voc
tem direitos sobre os objetos referenciados pelo procedimento. O Oracle verifica se o proprietrio (OWNER)
do procedimento tem permisses e isso durante a compilao do procedimento.
Desta forma, a PL/SQL disponibiliza atravs dos procedimentos armazenados, um timo recurso para
controle de permisso. Voc pode ter uma aplicao onde, apenas um usurio tem direito de incluir, alterar
e excluir registros. Dentro do schema desse usurio voc desenvolve rotinas que executa essas operaes
e ento, transmite o direito de executar os procedimentos aos usurio que desejar. Esses usurio s
conseguiro alterar os registros atravs da rotina e voc poder aplicar regras internas no procedimento,
como consistncias e gerao de logs.
Elaborado por Josinei Barbosa da Silva
Pgina 71 de 154
Treinamento
acotes
Um pacote uma coleo encapsulada de objetos de esquema relacionados.
Esses objetos podem incluir procedimentos, funes, variveis, constantes, cursores, tipos e
excees. Um pacote compilado e depois armazenado no dicionrio de dados do banco de dados como
um objeto de esquema.
Um uso comum dos pacotes para reunir todos os procedimentos, funes e outros objetos
relacionados que executam tarefas semelhantes. Por exemplo, voc pode agrupar todos os objetos
utilizados para gerar um resumo de vendas em um nico pacote.
Os pacotes contm subprogramas arma=enados, ou programas isolados, os quais so chamados
subprogramas do pacote. Esses subprogramas podem ser chamados de outro programa armazenado,
triggers, programas ou de qualquer um dos programas interativos da Oracle, tais como O SQL*Plus. Ao
contrrio dos subprogramas armazenados, o pacote em si no pode ser chamado ou aninhado, e os
parmetros no podem ser passados para ele.
:anta*ens do uso de %acotes
Os pacotes oferecem as seguintes vantagens:
Eles permitem organizar o desenvolvimento do seu aplicativo de forma mais eficiente com os
mdulos. Cada pacote facilmente entendido e as interfaces entre os pacotes so simples, claras e
bem definidas;
Eles permitem conceder os privilgios de forma eficiente;
As variveis public e os cursores de um pacote persistem durante a sesso, ou seja, todos os
cursores e procedimentos que so executados nesse ambiente podem compartilhar deles;
Eles permitem executar a sobrecarga nos procedimentos e funes;
Eles melhoram o desempenho carregando vrios objetos ao mesmo tempo. Assim sendo, as
chamadas subseqentes a subprogramas relacionados no pacote no requerem entrada/sada;
Eles promovem a reutilizao do cdigo por meio do uso das bibliotecas que contm os
procedimentos armazenados e as funes, eliminando assim a codificao redundante.
&strutura de um %acote
Geralmente um pacote tem dois componentes:
1. Especifica14o( Declara os tipos, as variveis, as constantes, as excees, os cursores e os
subprogramas que esto disponveis para uso.
2. orpo( Define totalmente as funes e os procedimentos e, assim, implementa a
especificao.
' es%eci1icao do %acote
A especificao do pacote contm declaraes public. sso significa que os objetos declarados no
pacote so acessveis de qualquer parte do pacote e so disponveis programas externos. Assim sendo,
todas as informaes que o aplicativo precisa para executar um programa armazenado esto contidas na
especificao do pacote.
A sintaxe do comando de especificao o seguinte:
CREATE [OR REPLACE] PACHAGE package_name
[AUT)ID {CURRENTFUSER |DEFINER}] {IS | AS}
[,$cS$L Bo%Q oBCct declaration]
Elaborado por Josinei Barbosa da Silva
Pgina 72 de 154
Treinamento
END [package_name];
Nessa sintaxe, as palavras-chave e os parmetros so os seguintes:
package_name o nome do pacote que o criador define. importante dar-lhe um nome que
seja significativo e represente o contedo do pacote;
AUTHID representa o tipo de direitos que voc quer invocar quando o pacote for executado.
Os direitos podem ser aquele de CURRENT_USER ou aquele do pacote DEFINER (Criador);
package body ob5ect declaration o lugar no qual voc lista os objetos que sero criados
dentro do pacote.
Abaixo temos um exemplo de uma declarao de pacote:
CREATE OR REPLACE PACHAGE pck_cliente AS
-- Author : JOSINEI
-- Created : 27/6/2007 17:45:36
-- Purpose : -- atualiza dados cliente

/*--------------------------------------------------------------------------*/
/* Public function and procedure declarations */
/*--------------------------------------------------------------------------*/

-- Procedimento para atualizar valor mdio de compras do cliente
PROCEDURE AtualizarMediaComprasMensal(pCodigoCliente NUMBER);

-- Procedimento para atualizar dados de maior compra do cliente
PROCEDURE AtualizarMaiorCompra(pCodigoCliente NUMBER);
-- Funo que retorna o endereo do cliente
FUNCTION EnderecoCliente(pCodigoCliente NUMBER) RETURN 7ARC)AR4;
END pck_cliente;
1
O cor%o do %acote
O corpo de um pacote contm a definio dos objetos public que voc declara na especificao. O
corpo tambm contm as outras declaraes de que so privadas do pacote e que esto acessveis apenas
para os objetos do corpo do pacote. Os objetos privados declarados no corpo do pacote no so acessveis
para outros objetos fora do pacote. Ao contrrio da especificao do pacote, a parte de declarao do corpo
do pacote pode conter corpos de subrotinas.
38O9T'4T&+ Se a especificao declarar apenas constantes e variveis, o corpo do pacote no
necessrio.
A sintaxe para criar o corpo (body) do pacote o seguinte:
CREATE [OR REPLACE] PACHAGE BOD< package_name {IS | AS}
[,$cS$L Bo%Q oBCct declaration]
END [package_name];
Nessa sintaxe, as palavras-chave e os parmetros so os seguinte:
package_name o nome do pacote definido pelo criador. importante dar-lhe um nome
significativo e que represente o contedo do corpo do pacote. Esse nome deve ser igual ao nome
Elaborado por Josinei Barbosa da Silva
Pgina 73 de 154
Treinamento
dado a package durante a criao da especificao;
package body ob5ect declaration o lugar no qual voc lista os objetos que sero criados
dentro do pacote.
Abaixo segue um exemplo de corpo de pacote que implementa as rotinas pblicas que declaramos na
especificao do pacote pck_cliente:
CREATE OR REPLACE PACHAGE BOD< pck_cliente AS
-- Declarao de variveis privadas
rCli cliente;RO(T<PE;
-- Procedimento para atualizar valor mdio de compras do cliente
PROCEDURE AtualizarMediaComprasMensal(pCodigoCliente NUMBER) IS
BEGIN
-- Recupera a mdia a partir da soma mensal
SELECT A7G(rmv_vl_total)
INTO rCli.cli_vl_mediacomprasmensal
FROM (SELECT rmv_in_mes=
rmv_in_ano=
su!(rmv_vl_total) rmv_vl_total
FROM resumo_mensal_venda
()ERE cli_in_codigo 3 pCodigoCliente
GROUP B< rmv_in_mes=
rmv_in_ano
);
-- Atualiza Mdia Compras Mensal
UPDATE cliente
SET cli_vl_mediacomprasmensal 3 rCli.cli_vl_mediacomprasmensal
()ERE cli_in_codigo 3 pCodigoCliente;
-- Finaliza transao confirmando alteraes
COMMIT;
END;
-- Procedimento para atualizar dados de maior compra do cliente
PROCEDURE AtualizarMaiorCompra(pCodigoCliente NUMBER) IS
BEGIN
-- Recupera a maior compra do cliente
SELECT MAX(nfv_vl_total)
INTO rCli.cli_vl_maiorcompra
FROM nota_fiscal_venda
()ERE cli_in_codigo 3 pCodigoCliente;
-- Atualiza Maior Compra do cliente
UPDATE cliente
SET cli_vl_maiorcompra 3 rCli.cli_vl_maiorcompra
()ERE cli_in_codigo 3 pCodigoCliente;
-- Finaliza transao confirmando alteraes
COMMIT;
END;
-- Funo que retorna o endereo do cliente
FUNCTION EnderecoCliente(pCodigoCliente NUMBER) RETURN 7ARC)AR4 IS
vEnderecoCliente 7ARC)AR4(4:N);
Elaborado por Josinei Barbosa da Silva
Pgina 74 de 154
Treinamento
BEGIN
-- Recupera o endereo concatenado do cliente
SELECT cli_st_endereco ##' G '##cli_st_cidade ##' G '##cli_st_uf
INTO vEnderecoCliente
FROM cliente
()ERE cli_in_codigo 3 pCodigoCliente;
-- Retorna o endereo concatenado
RETURN(vEnderecoCliente);
END;
END pck_cliente;
1
Observando o corpo desse pacote, vemos que todos procedimentos recebem o cdigo do cliente
como parmetro (e todos eles so pblicos se observarmos o exemplo de especificao). Poderamos ter
definido na especificao, uma varivel para receber esse cdigo e ento, em todos os procedimentos
utilizaramos o seu valor, que seria inicializado antes de executar os procedimentos, como veremos a
seguir.
5tili>ando os sub%ro*ramas e variveis de um %acote
Quando um pacote invocado, o Oracle executa trs etapas para execut-lo:
1. Verifica o acesso de usurio Confirma se o usurio tem a concesso do privilgio de
sistema EXECUTE;
2. Verifica a validade do pacote Verifica no dicionrio de dados e determina se o sub-
programa vlido. Se o objeto invlido, ele automaticamente recompilado antes de ser
executado;
3. Executa O sub-programa invocado executado.
Para referenciar os sub-programas e objetos do pacote, voc deve usar a notao de ponto, como
abaixo:
package_name.type_name para referenciar um tipo definido na especificao de um pacote;
package_name.variable_name para referenciar uma varivel declarada na especificao de
um pacote;
package_name.subprogram_name para invocar um procedimento declarado na especificao
do pacote e implementado no corpo do pacote.
Onde:
package_name o nome do pacote;
type_name o nome de um tipo;
variable_name o nome de uma varivel;
subprogram_name o nome de um procedimento interno do pacote.
Poderamos invocar um procedimento do pacote que utilizamos para exemplificar a especificao e o
corpo de um pacote, da seguinte maneira:
DECLARE
Elaborado por Josinei Barbosa da Silva
Pgina 75 de 154
Treinamento
vEndereco 7ARC)AR4(4:N);
BEGIN
pck_cliente.AtualizarMediaComprasMensal(56);
vEndereco :3 pck_cliente.EnderecoCliente(56);
END;
1
8anuteno dos %acotes
&stados de um %acote
O pacote considerado invlido se o seu cdigo-fonte ou qualquer objeto que ele referencia foi
excludo, alterado ou substitudo desde que a especificao do pacote foi recompilada pela ltima vez.
Quando um pacote se torna invlido, o Oracle tambm torna invlido todo objeto que referencia o pacote.
9ecom%ilando %acotes
Para recompilar um pacote voc deve usar o comando ALTER PACKAGE com a palavra-chave
compile. Essa recompilao explcita elimina a necessidade de qualquer recompilao implcita no runtime
e evita todos os erros associados de compilao no aps modificaes no pacote.
Quando recompila um pacote, voc tambm recompila todos os objetos definidos dentro do pacote. A
recompilao no altera a definio do pacote ou de nenhum de seus objetos.
Os seguintes exemplos recompilam apenas o corpo de um pacote. A segunda declarao recompila
todo o pacote, incluindo o corpo e a especificao:
ALTER PACHAGE pck_cliente COMPILE BOD<;
ALTER PACHAGE pck_cliente COMPILE PACHAGE;
Voc pode compilar todos os pacotes do schema usando o utilitrio dbms_utility da Oracle.
SQL> xcut dbms_utility.compile_schema(SC)EMA 3A'TSIL7A');
'epend>ncia de pacote
Durante o processo de recompilao de um pacote, o Oracle invalida todos os objetos dependentes.
Esses objetos incluem procedimentos armazenados ou pacotes que chamam ou referenciam os objetos
declarados na especificao recompilada. Quando o programa de outro usurio chama ou referencia um
objeto dependente antes de ser recompilado, o Oracle o recompila automaticamente no runtime.
Durante a recompilao do pacote, o Oracle determina se os objetos dos quais o corpo do pacote
depende so vlidos. Se algum desses objetos for invlido, o Oracle os recompila antes de recompilar o
corpo do pacote. Se a recompilao for bem-sucedida, ento o corpo d pacote se torna vlido. Se forem
detectados erros as mensagens de erro apropriadas so geradas e o corpo do pacote permanece invlido.
Elaborado por Josinei Barbosa da Silva
Pgina 76 de 154
Treinamento
Tri**ers
O que um Trigger$
Um trigger um bloco PL/SQL que associado a um evento especfico, armazenado em um banco
de dados e executado sempre que o evento ocorrer.
No Oracle Database possvel criar triggers de tipos diferentes, como por exemplo:
!riggers DML (Data Manipulation Language): !riggers tradicionais para tratamento de
operaes NSERT, UPDATE e DELETE, que o Oracle Database suporta h muitos anos.
Enstead-of: ntroduzidos na verso 8 do Oracle Database como um modo de possibilitar a
atualizao de determinados tipos de vie3s.
Data Definition Language (DDL): Disponvel a partir do Oracle Database 8i, muito utilizado
para auditoria de alteraes nos objetos de um schema.
Banco de Dados: A exemplo das triggers DDL, tambm foi disponibilizada na verso 8i e
utilizada nos eventos de banco de dados, tais como inicializao e fechamento do banco,
logon, logoff e etc..
Embora os triggers sejam classificados em tipos diferentes, a estrutura entre esses tipos so
semelhantes.
Neste treinamento, estudaremos os triggers DML.
Triggers D8!
Os triggers DML so os triggers tradicionais que podem ser definidos em uma tabela e so
executados, ou "disparados, em resposta aos seguintes eventos:
ncluso de uma linha em uma tabela
Atualizao de uma linha em uma tabela
Excluso de uma linha em uma tabela
Uma definio de trigger DML consiste das seguintes partes bsicas:
Evento que dispara o trigger
Tabela no qual o evento ocorrer
Condio opcional que controla a hora em que o trigger executado
Bloco PL/SQL contendo o cdigo a ser executado quando o trigger disparado
Assim como functions; procedures e packages, um trigger armazenado no banco de dados, ou seja,
um objeto do banco e sempre executado quando o evento para o qual ele definido ocorre. No importa
se o evento disparado por alguma digitao em uma declarao SQL usando SQL*Plus, executando um
programa client-server ou qualquer outra ferramenta pela qual seja possvel manipular dados da tabela.
Por isso, uma lgica para manipular o dado includo, atualizado ou alterado em uma tabela, sempre
ser executado se estiver no trigger.
Entre os usos mais comuns de triggers DML, podemos citar a gerao de primary key (PK) e o
registro de log de alterao.
Abaixo segue um exemplo muito comum de trigger que gera um log de alterao:
Elaborado por Josinei Barbosa da Silva
Pgina 77 de 154
Treinamento
CREATE OR REPLACE TRIGGER trg_log_preco_prod_iu
AFTER UPDATE ON preco_produto
FOR EAC) RO(
BEGIN
INSERT INTO log_preco_produto
(lpp_st_tipopreco
=lpp_in_codigo
=lpp_vl_unitario_anterior
=lpp_vl_unitario_novo
=lpp_dt_alteracao
=lpp_st_usuario)
7ALUES
(:OLD.ppr_st_tipopreco
=:OLD.pro_in_codigo
=:OLD.ppr_vl_unitario
=:NE(.ppr_vl_unitario
=S<SDATE
=USER);
END trg_log_preco_prod_iu;
1
Ti%os de triggers D8!
Os triggers DML podem ser classificados de duas maneiras diferentes: quando so disparados (com
relao declarao SQL) ou se eles disparam ou no para cada linha afetada pela declarao SQL de
disparo (nvel do disparo). Combinando essas classificaes, temos quatro tipos de triggers DML.
#uando os triggers so dis%arados$
Existem duas opes de "quando o trigger deve ser disparado: antes e depois.
Os before triggers so executados antes do disparo da declarao SQL e os after triggers so
executados depois do disparo da declarao SQL.
#uais so os n=veis do trigger$
Existem dois possveis nveis para um trigger DML:
Nvel de linha: o mais comum e executado uma vez para cada linha afetada pela SQL
que a disparou. Apenas triggers em nvel de linha tm acesso aos valores de dados dos
registros afetados (novos e antigos
Nvel de declarao: Pouco usada, executado apenas uma vez diante da execuo da SQL.
& os eventos do trigger D8!$
Podemos definir trs eventos para um trigger DML:
NSERT
UPDATE
DELETE
Se combinarmos esses eventos aos nveis e o momento de execuo (quando), teremos FG ti%os de
triggers D8!@
Elaborado por Josinei Barbosa da Silva
Pgina 78 de 154
Treinamento
38O9T'4T&+ Os triggers definidos de forma idntica so executados sem ordem determinada. Se
voc escrever diversos triggers que so disparados antes de uma linha ser atualizada, por exemplo, deve
garantir que a integridade do banco de dados no dependa da ordem de execuo.
Dicas+
Use os triggers no nFvel de linha antes da atuali=a14o para a implanta14o de
regras de neg/cio comple0as e c)lculos comple0os; pois provavelmente voc>
dese5a fa=er isso antes @ue a linha se5a inseridaC
Use os triggers no nFvel de linha ap/s a atuali=a14o para logging das altera12esC
Use os triggers no nFvel de declara14o antes da atuali=a14o para implementar
regras de seguran1a @ue n4o dependam dos valores dos registros afetadosC
No implemente regras de neg/cio diretamente nos triggers. Prefira implementar
regras em stored procedures e apenas e0ecut)-las a partir das triggersC
NUNCA utili=e triggers para implementar integridade referencial. Para isso e0iste
as constraints no banco de dados.
Ativando e desativando triggers
Os triggers podem ser temporariamente desativados sem precisar exclu-los e depois recri-los. sso
pode ser til quando voc precisar fazer algum processamento especial, como por exemplo, uma carga de
dados. O comando ALTER TRGGER usado para ativar e desativar triggers. Veja o exemplo abaixo:
GG Desabilitar t&iLL&
ALTER TRIGGER rep_trg_iu DISABLE;
GG Habilitar t&iLL&
ALTER TRIGGER rep_trg_iu ENABLE;
Dica: Executar carga de dados com triggers desabilitados pode representar um grande ganho de
desempenho, mas cada caso deve ser analisado, pois qualquer regra implementada nos triggers
desabilitados, sero ignoradas.
Elaborado por Josinei Barbosa da Silva
Pgina 79 de 154
Treinamento
&xerc=cios ro%ostos
1. Desenvolva uma funo armazenada que receba como argumento o nmero de uma nota
fiscal e retorne o valor total dos itens (coluna NFV_VL_TOTAL da tabela de itens). Nomeie essa
funo como fnValorTotalItensNotaFiscal e teste utilizando uma declarao SQL simples
na tabela DUAL.
2. Desenvolva um procedimento armazenado que busque, para cada nota fiscal cadastrada, o
valor total de seus itens e valide se o seu valor atual (coluna NFV_VL_TOTAL) corresponde ao total
dos itens e, caso no corresponda, atualize esse valor. Utilize a funo criada no primeiro exerccio
para recuperar o valor total dos itens. Nomeie esse procedimento com
prAtualizarValorNotaFiscal.
3. Crie um pacote chamado pck_NotaFiscalVenda e implemente nele a funo criada no
primeiro exerccio e o procedimento criado no segundo exerccio. Tanto a funo quanto o
procedimento, devem ser publicados na especificao do pacote. Adicione nesse pacote, um
procedimento que valide se o valor total do item est certo e, caso no esteja, corrija. O valor
correto do item obtido multiplicando a quantidade (NFV_QT_FATURADA) pelo valor unitrio
(NFV_VL_UNTARO) e subtraindo o desconto (NFV_PE_DESCONTO). Esse novo procedimento
no deve ser pblico e o procedimento prAtualizarValorNotaFiscal deve execut-lo antes de
atualizar o valor total da nota.
4. Crie uma procedure que receba dois argumentos: um ano e um ms. Esse procedimento
deve recuperar as notas fiscais do perodo informado (ano/ms) e atualizar a tabela de resumo
mensal de vendas para cada produto/cliente/representante, sendo que, se o perodo j existir, deve
ser atualizado. Nomeie a procedure como prAtualizarVendasMensal.
5. Crie um pacote chamado pck_cliente que contenha:
Um procedimento pblico que receba como argumento o cdigo do cliente e atualize:
O valor da maior compra (coluna CL_VL_MAORCOMPRA), obtida na tabela de nota
fiscal de vendas;
A data da maior compra (coluna CL_DT_MAORCOMPRA), obtida na tabela de nota
fiscal de vendas;
A mdia de compras mensal (coluna CL_VL_MEDACOMPRASMENSAL), obtida na
tabela de resumo mensal de vendas.
Uma funo pblica que receba como argumento o cdigo do cliente e retorne o cdigo
do produto com maior freqncia de compra (atravs dos itens de nota fiscal ou da tabela de
resumo mensal);
Uma funo pblica que receba como argumento o cdigo do cliente e retorne o cdigo
do produto com maior representatividade financeira nas compras do cliente.
6. Crie um pacote chamado pck_representante que contenha:
Um procedimento pblico que receba como argumento o cdigo do representante e
atualize:
Elaborado por Josinei Barbosa da Silva
Pgina 80 de 154
Treinamento
O valor da maior venda (coluna REP_VL_MAORVENDA), obtida na tabela de nota
fiscal de vendas;
A data da maior venda (coluna REP_DT_MAORVENDA), obtida na tabela de nota
fiscal de vendas;
A mdia mensal de venda (coluna REP_VL_MEDAMENSALVENDAS), obtida na
tabela de resumo mensal de vendas;
Uma funo privada do pacote que calcule a meta mensal de vendas, tirando a mdia
de vendas dos ltimos trs meses adicionados de 5%;
Um procedimento pblico que atualize a meta mensal do representante (coluna
REP_VL_METAMENSAL) utilizando a funo criada.
7. Desenvolva um pacote chamado pck_produto, com procedimentos e funes necessrios
para atualizar:
Data e valor de ltima venda de cada produto;
Data e valor de maior venda de cada produto.
Elaborado por Josinei Barbosa da Silva
Pgina 81 de 154
Treinamento
04 Collections
1. Tabelas %or =ndice;
2. Tabelas anin/adas;
3. 'rra,s;
4. Collection como 1onte de dados 6S&!&CT no 'rra,7;
5. BulH Bindin*
Elaborado por Josinei Barbosa da Silva
Pgina 82 de 154
Treinamento
Colees
As colees da PL/SQL permitem agrupar muitas ocorrncias de um objeto. A PL/SQL disponibiliza
trs tipos de colees:
Tabelas por ndice;
Tabelas aninhadas;
Arrays de tamanho variado (varray).
Se voc conhece as outras linguagens de programao, pense em uma coleo como algo
semelhante a um array (Um vetor). Um array uma srie de elementos repetidos, todos do mesmo tipo.
Voc vai aprender que alguns tipos de coleo da Oracle so mais parecidos com arrays tradicionais do que
outros.
Tabelas %or =ndice
As tabelas por ndice so um dos trs tipos de coleo suportados pela PL/SQL. Na verdade, elas
so o tipo de coleo original. Nas primeiras verses da PL/SQL, as tabelas por ndices eram o nico tipo
de coleo disponvel.
Uma tabela por ndice uma tabela de elementos mantida em memria, onde cada elemento
indexado por um valor de inteiro. As tabelas por ndice funcionam da mesma forma que os arrays, com
algumas diferenas:
Uma tabela por ndice pode ser populada esparsamente;
Voc no define um tamanho mximo para uma tabela por ndice.
Declarando uma tabela %or =ndice
Para declarar uma tabela por ndice voc deve:
1. Definir um tipo, especificando um datatype para a coleo e outro para o ndice da tabela,
sendo que, o datatype da coleo pode ser um tipo escalar, tal como um NUMBER ou VARCHAR2, ou
ele pode ser um tipo composto, tal como um registro e o datatype do ndice da tabela deve sempre
ser BINARY_INTEGER.
2. Declarar uma varivel do tipo definido.
A sintaxe para declarar um tipo para tabela de ndice o seguinte:
T<PE type_name IS TABLE OF data_type [NOT NULL] INDEX B< BINAR<FINTEGER;
Onde:
type_name o nome do tipo que voc est declarando (Ele ser usado para definir o tipo das
variveis);
data_type o tipo de dado da coleo, ou seja, cada elemento da tabela armazena um valor
desse tipo;
&$! &%LL probe que uma entrada da tabela contenha valor nulo.
Abaixo, segue um exemplo de definio de tipos para coleo e declarao de variveis desses tipos:
DECLARE
-- Definio de tipos
Elaborado por Josinei Barbosa da Silva
Pgina 83 de 154
Treinamento
T<PE TArrayNumerico IS TABLE OF NUMBER INDEX B< BINAR<FINTEGER;
T<PE TRepresentante IS TABLE OF representante;RO(T<PE INDEX B< BINAR<FINTEGER;
-- Declarao de variveis
cOrdemRepresentante TArrayNumerico;
cRepresentante TRepresentante;
BEGIN
NULL;
END;
1
No exemplo acima, a varivel c$rdem"epresentante como um array de uma nica coluna do tipo
number e a varivel c"epresentante como uma matriz de duas dimenses, ou seja, um array contendo
todas as colunas da tabela representante.
8ani%ulando uma tabela %or =ndice
nserindo entradas em uma tabela por ndice
Os elementos de uma tabela por ndice so identificados exclusivamente por um valor de inteiro, ou
ndice. Sempre que referencia um valor da tabela, voc deve fornecer o ndice daquele valor. Para inserir os
valores em uma tabela PL/SQL, voc usa uma declarao de atribuio como esta:
t$B/F.$&(in%x) :3 .$/u;
Onde:
table_var o nome da varivel do tipo tabela por ndice;
inde0 o valor de inteiro que indica o ndice da entrada e pode ser qualquer nmero entre 1 e
2.147.483.647, no precisando ser consecutivos, ou seja, se voc precisasse colocar apenas duas
entradas em uma tabela, poderia usar os ndices 1 e 2 ou poderia usar 1 e 2.147.483.647,
ocupando em ambas situaes, duas entradas apenas (A PL/SQL no reserva espao para as
entradas que no so usadas);
value o valor a ser atribudo para a entrada.
No exemplo abaixo, definimos um tipo de tabela por ndice para conter o nome dos clientes. A partir
desse tipo, declaramos uma varivel que conter o nome de cada um dos clientes:
DECLARE
-- declarao de cursores
CURSOR cs_cliente IS
SELECT c.*
FROM cliente c;

-- declarao de tipos
T<PE TNomeCliente IS TABLE OF 7ARC)AR4(5649) INDEX B< BINAR<FINTEGER;

-- declarao de variveis
cCli TNomeCliente;
vCount BINAR<FINTEGER :3 6;
BEGIN
-- Percorrer todos os clientes do cadastro
Elaborado por Josinei Barbosa da Silva
Pgina 84 de 154
Treinamento
FOR csc IN cs_cliente LOOP
-- Incrementa contador
vCount :3 vCount P 5;
-- Atribuio de valores
cCli(vCount) :3 csc.cli_st_nome;
END LOOP;
END;
1
No exemplo acima, utilizamos um contador para gerar o ndice, mas poderamos utilizar o prprio
cdigo do cliente como ndice, uma vez que ele numrico e o ndice no precisa ser utilizado na
seqncia, como no exemplo abaixo:
DECLARE
-- declarao de cursores
CURSOR cs_cliente IS
SELECT c.*
FROM cliente c;

-- declarao de tipos
T<PE TNomeCliente IS TABLE OF 7ARC)AR4(5649) INDEX B< BINAR<FINTEGER;

-- declarao de variveis
cCli TNomeCliente;
BEGIN
-- Percorrer todos os clientes do cadastro
FOR csc IN cs_cliente LOOP
-- Atribuio de valores
cCli(csc.cli_in_codigo) :3 csc.cli_st_nome;
END LOOP;
END;
1
Referenciando valores em uma tabela por ndice
Para referenciar uma entrada especfica em uma tabela por ndice, voc especifica um valor de
ndice, usando a mesma sintaxe do tipo array que usou ao inserir os dados. Por exemplo, para avaliar se o
cliente da entrada 2 novo, basta escrever uma declarao IF como esta:
IF cCli(4).Novo T)EN
...
...
END IF;
ou
IF cCli(4).Novo 3 TRUE T)EN
...
...
END IF;
G"eferencie um valor do tipo B$$LE*& como preferirA7
Como vimos anteriormente, as tabelas por ndice so populadas esparsamente e por isso pode haver
Elaborado por Josinei Barbosa da Silva
Pgina 85 de 154
Treinamento
valores de ndices para os quais no h entrada. Se tentar referenciar um deles, voc tem um exceo
NO_DATA_FOUND e, conseqentemente, poder tratar essa exceo. Caso no queira utilizar a rea de
EXCEPTION para tratar essa exceo, voc pode utilizar o mtodo EXISTS da coleo, como mostrado
abaixo:
IF cCli.EXISTS(5:) T)EN
...
...
END IF;
Um mtodo uma funo ou um procedimento que anexado a um objeto. Neste caso, a tabela cCli
o objeto e a funo EXISTS o mtodo.
Alterando entradas de tabela
Voc atualiza uma tabela PL/SQL de modo semelhante ao que voc faz para inserir. Se voc j
inseriu um nmero de linha 10 na tabela cCli (usada no nosso exemplo), ento voc pode atualizar aquela
mesma linha com uma declarao como esta:
cCli(56).Nome :3 'G&u,o C$&&+ou&';
Essa declarao atualiza o campo &ome do registro na entrada da tabela de nmero 10, com o texto
'Grupo Carrefour'.
Excluindo entradas de tabela
Voc pode excluir entradas de uma tabela invocando o mtodo DELETE, que pode excluir uma
entrada, um intervalo de entradas ou todas as entradas, da tabela. A sintaxe para o mtodo DELETE a
seguinte:
table_name.DELETE[(primeira_entrada[=ultima_entrada])];
onde:
table_name o nome da varivel de tabela;
primeira_entrada o ndice da entrada que voc quer excluir ou o ndice da primeira entrada
de um intervalo de entradas que voc quer excluir;
ultima_entrada o ltimo ndice de um intervalo de entradas que voc quer excluir.
38O9T'4T&+ A invocao do mtodo DELETE sem a passagem de nenhuma argumento, faz com
que todos os dados sejam excludos da tabela.
Ento:
Para limpar toda tabela cCli, basta executar a declarao:
cCli.DELETE;
Para apagar apenas a entrada 8 da tabela cCli, basta executar a declarao:
cCli.DELETE(R);
E para apagar as entradas de 6 a 10 da tabela cCli, execute a declarao:
cCli.DELETE(N=56);
Elaborado por Josinei Barbosa da Silva
Pgina 86 de 154
Treinamento
Tabelas anin/adas
As tabelas aninhadas surgiram no Oracle 8. No banco de dados, uma tabela aninhada uma coleo
no ordenada de linhas. Pense em um sistema de notas fiscais, onde cada nota contenha um nmero de
itens. Tradicionalmente, os criadores de banco de dados o implementariam usando duas tabelas de banco
de dados, uma para as notas e uma segunda para os itens. Cada registro de item conteria um vnculo de
chave estrangeira com sua "nota me. A partir do Oracle 8, possvel que cada nota tenha sua prpria
tabela de item e que aquela tabela seja armazenada como uma coluna.
A PL/SQL suporta as tabelas aninhadas porque o banco de dados Oracle as suporta. As tabelas
aninhadas so declaradas e usadas de forma semelhante s tabelas por ndice, mas existem algumas
diferenas que voc deve conhecer:
Quando voc recupera uma tabela aninhada do banco de dados, as entradas so indexadas
consecutivamente. Mesmo ao construir uma tabela aninhada dentro da PL/SQL, voc no pode
pular arbitrariamente os valores de ndice como faria com as tabelas por ndice;
As tabelas aninhadas no suportam os datatypes especficos da PL/SQL;
Voc precisa usar um mtodo construtor para inicializar uma tabela aninhada;
O intervalo de ndices das tabelas aninhadas de -2.147.483.647 at 2.147.483.647 (Os
ndices das tabelas por ndice no podem ser 0 ou nmeros negativos).
38O9T'4T&+ Quando as tabelas aninhadas esto no banco de dados, as linhas no tm nenhuma
ordem determinada associada a elas. Quando voc l uma tabela aninhada na PL/SQL, cada linha recebe
um ndice. Assim, de certa forma, naquele ponto h uma certa ordem envolvida. Entretanto, a ordem no
preservada. Se voc selecionar a mesma tabela aninhada duas vezes, voc pode ter uma ordem de linha
diferente a cada vez.
C53D'DO+ At a verso 8i do Oracle Database, tabela aninhada s estava disponvel na distribuio
Enterprise do Oracle Database, por isso, certifique-se que seu cliente possui esse recurso antes de utiliz-
lo.
Se as tabelas aninhadas fossem criadas primeiro, a Oracle nunca teria desenvolvido o tipo por ndice.
Entretanto, ambas esto disponveis e voc deve selecionar entre elas. Se voc precisar de um array de um
datatype especfico da PL/SQL, tal como BOOLEAN, NATURAL ou NTEGER, ento sua nica escolha
uma tabela por ndice. A outra coisa que deve ser considerada que as tabelas aninhadas requerem que os
seus ndices sejam consecutivos, tal como 1,2,3 e assim por diante.
Declarando uma tabela anin/ada
Voc declara uma tabela aninhada usando as duas mesmas etapas que usa para declarar uma tabela
por ndice. Em primeiro lugar, voc declara um tipo, e depois declara uma ou mais variveis daquele tipo.
A sintaxe para criar o tipo a seguinte:
T<PE type_name IS TABLE OF data_type [NOT NULL];
onde:
type_name o nome do tipo que voc est declarando;
data_type o datatype da coleo;
&$! &%LL probe que uma entrada de tabela seja nula.
Elaborado por Josinei Barbosa da Silva
Pgina 87 de 154
Treinamento
38O9T'4T&+ O datatype de uma coleo NO pode ser BOOLEAN, NCHAR, NCLOB, NVARCHAR2,
REF CURSOR, TABLE e VARRAY;
Como pode ser observado, a declarao de um tipo de uma tabela aninhada semelhante quela
usada para uma tabela por ndice, mas a clusula INDEX BY no usada. A presena de uma clusula
INDEX BY que indica para o Oracle que voc est utilizando uma tabela por ndice.
Depois de declarar um tipo de tabela aninhada, voc pode usar aquele tipo para declarar as variveis
de tabela.
8ani%ulando tabelas anin/adas
nicializando uma tabela aninhada
Quando voc declara uma varivel para uma tabela aninhada, voc tem uma varivel que no contm
nada. Ela considerada nula. Para torn-la uma tabela, voc precisa chamar uma funo construtora para
criar aquela tabela e, depois, armazenar o resultado daquela funo na varivel que voc est usando. Por
exemplo, digamos que voc tenha usado as seguintes declaraes para criar uma tabela aninhada
chamada cRep:
T<PE TRepresentante IS TABLE OF representante;RO(T<PE;
cRep TRepresentante;
Para usar realmente cRep como uma tabela aninhada, voc precisa chamar uma funo construtora
para inicializ-la. A funo construtura sempre toma seu nome do nome do tipo usado para declarar a
tabela; portanto, nesse caso, a funo construtora se chamaria Trepresentante. Esse conceito vem do
mundo orientado a ob5etos, onde um construtor a funo que realmente aloca memria para um objeto.
Voc pode chamar a funo construtora sem passar nenhum argumento e criar uma tabela vazia, como no
exemplo seguinte:
cRep :3 TRepresentante();
Voc tambm pode chamar a funo construtora e passar valores para uma ou mais entradas.
Entretanto, os valores listados no construtor devem ser do mesmo tipo da tabela. sso um pouco difcil de
fazer com os registros. O exemplo seguinte mostra como declarar cRep como uma tabela de registros de
representante e depois a inicializa com dois representantes:
DECLARE
-- Definio de tipos
T<PE TRepresentante IS TABLE OF representante;RO(T<PE;

-- Declarao de variveis
cRep TRepresentante;
rRep1 representante;RO(T<PE;
rRep2 representante;RO(T<PE;
BEGIN
-- Atribuindo dados do primeiro representante
rRep1.rep_in_codigo :3 56;
rRep1.rep_st_nome :3 'Su M$%&uL$';

-- Atribuindo dados do segundo representante
rRep2.rep_in_codigo :3 46;
rRep2.rep_st_nome :3 'CK$.s';
Elaborado por Josinei Barbosa da Silva
Pgina 88 de 154
Treinamento

-- Inicializando a coleo de representante com duas entradas: rRep1 e rRep2
cRep :3 TRepresentante(rRep1= rRep2);
END;
1
O segredo nesse exemplo que os argumentos para o construtor so rRep1 e rRep2.
Ambos so registros do tipo representante%ROWTYPE e coincidem com o tipo de elemento da tabela.
Obviamente um pouco trabalhoso definir as coisas dessa forma.
Para adicionar entradas a uma tabela alm daquelas que voc criou com o construtor, voc precisa
estender a tabela como discutimos na seo seguinte.
Estendendo uma tabela aninhada
Para estender uma tabela aninhada de modo a adicionar mais entradas a ela, use o mtodo e0tend.
O mtodo e0tend permite que voc adicione uma ou vrias entradas. Ele tambm permite que voc clone
uma entrada existente uma ou mais vezes. A sintaxe do mtodo e0tend a seguinte:
colecao.EXTEND[(quantidade_entradas[=entrada_clone])];
onde:
colecao o nome da tabela aninhada;
@uantidade_entradas o nmero de entradas novas que voc quer adicionar;
entrada_clone uma varivel ou constante que indica qual entrada voc quer clonar.
A seguir temos um exemplo que utiliza o mtodo e0tend para estender a coleo de representantes:
DECLARE
-- Declarao de cursores
CURSOR cs_representante IS
SELECT r.*
FROM representante r;

-- Definio de tipos
T<PE TRepresentante IS TABLE OF representante;RO(T<PE;

-- Declarao de variveis
cRep TRepresentante;
vRepCount PLSFINTEGER :36;
BEGIN
-- Inicializa a coleo criando uma entrada vazia
cRep :3 TRepresentante();

-- Adiciona uma entrada na coleo para cada representante do cadastro
FOR csr IN cs_representante LOOP
vRepCount :3 vRepCount P 5;
cRep.EXTEND;
cRep(vRepCount).rep_in_codigo :3 csr.rep_in_codigo;
cRep(vRepCount).rep_st_nome :3 csr.rep_st_nome;
cRep(vRepCount).rep_vl_maiorvenda :3 csr.rep_vl_maiorvenda;
END LOOP;

Elaborado por Josinei Barbosa da Silva
Pgina 89 de 154
Treinamento
-- Clona a primeira entrada 5 vezes
cRep.EXTEND(:=5);

-- Exibe todos os registros da coleo
FOR i IN 5..(vRepCount P :) LOOP
dbms_output.put_line('R,&snt$nt2 '##cRep(i).rep_in_codigo ##chr(58)##
'No!'''''''''2 '##cRep(i).rep_st_nome ##chr(58)##
'M$io& 7n%$''2 '##cRep(i).rep_vl_maiorvenda##chr(58)
);
END LOOP;
END;
1
Removendo entradas de uma tabela aninhada
Voc pode remover entradas de uma tabela aninhada usando o mtodo delete, assim como faz com
as tabelas por ndice. O exemplo abaixo exclui a entrada 10 da coleo cRep:
cRep.DELETE(56);
Voc pode usar as entradas depois de exclu-las. As outras entradas da tabela no so renumeradas.
Outro mtodo para remover as linhas de uma tabela aninhada invocar o mtodo trim. O mtodo
trim remove um nmero especificado de entradas do final da tabela.
Sintaxe:
colecao.TRIM[(quantidade_entradas)];
Onde:
colecao o nome da tabela aninhada;
@uantidade_entradas o nmero de entradas a serem removidas do final. O padro 1.
O mtodo trim se aplica apenas s tabelas aninhadas e aos arrays de tamanho variado. Ele no pode
ser aplicado s tabelas por ndice.
Para exemplificar a remoo de entradas de uma coleo, vamos alterar o exemplo anterior para,
aps exibir as entradas, remover as cinco que foram clonadas usando o mtodo trim e apagar a entrada 1
usando o mtodo delete:
DECLARE
-- Declarao de cursores
CURSOR cs_representante IS
SELECT r.*
FROM representante r;

-- Definio de tipos
T<PE TRepresentante IS TABLE OF representante;RO(T<PE;

-- Declarao de variveis
cRep TRepresentante;
vRepCount PLSFINTEGER :36;
BEGIN
-- Inicializa a coleo criando uma entrada vazia
cRep :3 TRepresentante();

-- Adiciona uma entrada na coleo para cada representante do cadastro
FOR csr IN cs_representante LOOP
Elaborado por Josinei Barbosa da Silva
Pgina 90 de 154
Treinamento
vRepCount :3 vRepCount P 5;
cRep.EXTEND;
cRep(vRepCount).rep_in_codigo :3 csr.rep_in_codigo;
cRep(vRepCount).rep_st_nome :3 csr.rep_st_nome;
cRep(vRepCount).rep_vl_maiorvenda :3 csr.rep_vl_maiorvenda;
END LOOP;

-- Clona a primeira entrada 5 vezes
cRep.EXTEND(:=5);

-- Exibe todos os registros da coleo
FOR i IN 5..(vRepCount P :) LOOP
dbms_output.put_line('R,&snt$nt2 '##cRep(i).rep_in_codigo ##chr(58)##
'No!'''''''''2 '##cRep(i).rep_st_nome ##chr(58)##
'M$io& 7n%$''2 '##cRep(i).rep_vl_maiorvenda##chr(58)
);
END LOOP;

dbms_output.put_line(cRep.COUNT##' nt&$%$s ncont&$%$sJ');

-- Remove as 5 entradas clonadas
cRep.TRIM(:);

-- Exclui a primeira entrada
cRep.DELETE(5);
-- Exibe a nova contagem de entradas
dbms_output.put_line('ALo&$ &st$! '##cRep.COUNT##' nt&$%$sJ');

-- Exibe as entradas que restaram
FOR i IN 5..(vRepCount P :) LOOP
IF cRep.EXISTS(i) T)EN
dbms_output.put_line('R,&snt$nt2 '##cRep(i).rep_in_codigo ##chr(58)##
'No!'''''''''2 '##cRep(i).rep_st_nome ##chr(58)##
'M$io& 7n%$''2 '##cRep(i).rep_vl_maiorvenda##chr(58)
);
END IF;
END LOOP;
END;
1
Nesse exemplo, o mtodo trim usado para remover os cinco clones que criamos no exemplo
anterior. Logo em seguida, o mtodo delete chamado para excluir tambm a primeira entrada. Depois do
uso do mtodo delete, utilizamos o mtodo count para exibir o nmero de entradas. At a verso 8.1.5 a
PL/SQL s reconhecia a excluso das entradas aps referenciar a contagem da coleo, ou seja, o uso do
mtodo count servia para desviar de um bug do Oracle. Por fim, as entradas restante so exibidas.
'rra,s de taman/o varivel
Assim como as tabelas aninhadas, os arrays de tamanho varivel ou varrays tambm comearam a
existir a partir da verso Oracle8. Os arrays so semelhantes s tabelas aninhadas, mas eles possuem um
tamanho mximo fixo. Eles diferem das tabelas aninhadas porque quando voc armazena um varray em
uma coluna de banco de dados, a ordem dos elementos preservada.
Elaborado por Josinei Barbosa da Silva
Pgina 91 de 154
Treinamento
C53D'DO+ At a verso 8i do Oracle Database, array de tamanho varivel s estava disponvel na
distribuio Enterprise do Oracle Database, por isso, certifique-se que seu cliente possui esse recurso
antes de utiliz-lo.
Declarando e iniciali>ando um :'99'D
Para criar um varray; voc usa a palavra-chave VARRAY em uma declarao de tipo para criar um tipo
de array. Depois voc pode usar aquele tipo para declarar uma ou mais variveis. A sintaxe para declarar
um tipo varray a seguinte:
T<PE nome_tipo IS 7ARRA< (size) OF tipo_entrada [NOT NULL];
Onde:
nome_tipo o nomo do tipo de array;
si=e o nmero de elementos que voc quer que o array contenha;
tipo_entrada o tipo de dados dos elementos do array;
&$! &%LL probe que as entradas do array sejam nulas.
Os varray precisam ser inicializados assim como as tabelas aninhadas. Antes de usar um VARRAY,
voc precisa chamar seu construtor. Voc pode passar os valores para o construtor e aqueles valores so
usados para criar elementos de array, ou voc pode invocar o construtor sem nenhum parmetro para criar
um array vazio.
No exemplo abaixo, inicializamos uma varivel VARRAY chamada vStrings, incluindo duas entradas:
DECLARE
-- Definio de tipos
T<PE TStrings IS 7ARRA<(5666666) OF 7ARC)AR4(5649);

-- Declarao de variveis
vStrings TStrings;
BEGIN
-- Inicializa a coleo criando uma entrada vazia
vStrings :3 TStrings('LinK$ 5'='LinK$ 4');

-- Exibe todos os registros da coleo
FOR i IN 5..4 LOOP
dbms_output.put_line(vStrings(i));
END LOOP;
END;
1
Neste exemplo, criamos um tipo VARRAY chamado TStrings e declaramos a varivel vStrings como
sendo do tipo TStrings. Na seo de execuo do bloco, inicializamos vStrings com duas linhas, contendo
na primeira entrada o texto 'Linha 1' e na segunda entrada, o texto 'Linha 2'. No final, temos um LOOP com
dois ciclos que imprimem na tela as duas entradas.
'dicionando e removendo dados de um :'99'D
Depois de inicializar um VARRAY, voc pode adicionar e remover dados dele, assim como faz com
uma tabela aninhada. Se voc quiser adicionar ao array mais elementos do que voc criou quando o
inicializou, pode chamar o mtodo e0tend. Entretanto, voc s pode estender um array at o tamanho
mximo especificado na definio do tipo array.
Elaborado por Josinei Barbosa da Silva
Pgina 92 de 154
Treinamento
Para remover entradas de um VARRAY, basta utilizar os mtodos delete ou trim, como fizemos na
manipulao de tabelas aninhadas.
O exemplo abaixo mostra o contedo da tabela produto sendo lido em um VARRAY:
DECLARE
-- Declarao de cursores
CURSOR cs_produto IS
SELECT p.pro_in_codigo=
p.pro_st_nome=
p.pro_st_marca=
p.pro_dt_ultimavenda
FROM produto p;

-- Definio de tipos
T<PE TProduto IS 7ARRA<(566) OF cs_produto;RO(T<PE;

-- Declarao de variveis
cProd TProduto;
vIndice PLSFINTEGER :3 6;
BEGIN
-- Inicializar coleo
cProd :3 TProduto();

-- Abrir cursor
OPEN cs_produto;

-- Para cada produto, atribuir cdigo, nome, marca e data de ultima venda
LOOP
-- Incrementar indice e estender coleo
vIndice :3 vIndice P 5;
cProd.EXTEND;

-- Recuperar dados do cursor e atribuir para coleo
FETC) cs_produto INTO cProd(vIndice);
-- Se no encontrar mais nada no cursor, elimina a ltima entrada
-- gerada na coleo, decrementa o ndice da colo e abandona o LOOP
IF cs_produto;NOTFOUND T)EN
cProd.TRIM(5);
vIndice :3 vIndice G 5;
EXIT;
END IF;

END LOOP;

-- Fechar cursor
CLOSE cs_produto;

-- Imprimir cada entrada da coleo na tela
FOR i IN 5..vIndice LOOP
dbms_output.put_line('CM%'P&o%''''2 '##cProd(i).pro_in_codigo##chr(58)##
'No!''''''''2 '##cProd(i).pro_st_nome##chr(58)##
'M$&c$'''''''2 '##cProd(i).pro_st_marca##chr(58)##
'U/ti!$ 7n%$2 '##cProd(i).pro_dt_ultimavenda##chr(58)
);
Elaborado por Josinei Barbosa da Silva
Pgina 93 de 154
Treinamento
END LOOP;
END;
1
Neste exemplo, para cada produto retornado no cursor, utilizamos o mtodo e0tend para incluir uma
entrada na coleo e, no final, quando o comando FETCH no encontra mais nenhum registro no cursor,
utilizamos o mtodo trim para remover a ltima entrada da coleo que foi gerada pelo prprio comando
FETCH.
Observe que e0tend no pode ser usado para aumentar o array alm do tamanho mximo
especificado de 100 entradas.
8todos de tabela da !"S#!
Ns j estudamos alguns mtodos de coleo nas sees anteriores. A PL/SQL fornece vrios outros
mtodos incorporados para uso com as tabelas. Veja a tabela a seguir:
Tabela 6: $?todos de tabela da PL/SQL
Mtodo Descrio Exempo
count O mtodo count retorna o nmero
de entradas da tabela.
vRecordCount := cCli.count
exist Essa funo recebe o nmero da
entrada como argumento e retorna
o valor TRUE se a entrada
existir. Caso contrrio, ela
retorna FALSE.
IF cCli.exists(15) THEN
...
END IF;
limit Esse mtodo retorna o nmero
mximo de elementos que uma
coleo pode conter. Somente
arrays de tamanho varivel tm um
limite superior. Quando usado com
as tabelas aninhadas e com as
tabelas por ndice, o mtodo
limit retorna NULL.
IF vIndice > cli_array.limit THEN
...
ELSE
cli_array(vIndex) := 10;
END IF;
first Esse mtodo retorna o valor de
ndice mais baixo usado em uma
coleo.
vMenorIndiceValido := cCli.first;
last Essa funo retorna o valor do
ndice mais alto usado na tabela.
vMaiorIndiceValido := cCli.last;
next O mtodo next retorna o valor do
prximo ndice vlido que seja
maior do que um valor que voc
fornece.
vProximoIndice := cCli.Next(vIndiceCorrente)
prior O mtodo prior retorna o valor do
ndice vlido mais alto que
precede o valor do ndice que
voc fornecer.
vIndiceAnterior := cCli.Prior(vIndiceCorrente)
delete O mtodo delete permite excluir
entrada de uma coleo. Voc pode
excluir todas as entradas ou uma
entrada especfica ou um
intervalo de entradas.
O mtodos delete um
procedimento e no retorna um
valor.
-- Excluindo todas as entradas
cCli.delete;
-- Excluindo apenas uma entrada
cCli.delete(8);
-- Excluindo um intervalo de entradas
cCli.delete(6,10);
trim O mtodo trim permite excluir
entradas do final de uma coleo.
Voc pode usar trim apenas em uma
-- cortando uma entrada
cli_array.trim;
Elaborado por Josinei Barbosa da Silva
Pgina 94 de 154
Treinamento
Mtodo Descrio Exempo
entrada ou em diversas entradas.
O mtodo trim um procedimento e
no retorna uma valor.
Este mtodo s pode ser usado com
arrays de tamanho variado e com
as tabelas aninhadas.
-- cortando 3 emtradas
cli_array.trim(3);
extend O mtodo extend permite adicionar
entradas no final de uma coleo.
Voc pode adicionar uma entrada
ou vrias entradas, especificando
o nmero a ser adicionado como
parmetro.
O mtodo extend tambm pode ser
usada para clonar entradas
existentes. Voc clona uma
entrada passando o nmero da
entrada como um segundo parmetro
para extend.
O mtodo extend um procedimento
e no retorna um valor.
Assim como trim, extend s pode
ser usado com os arrays de
tamanho varivel e com as tabelas
aninhadas.
-- Adicionando uma entrada
cli_array.extend;
-- Adicionando trs entradas
cli_array.extend(3);
-- Adicionando uma nova entrada clone da 3
cli_array.extend(1,3);
&xecutando declaraes S&!&CT em uma coleo
Em PL/SQL voc consegue executar uma declarao SELECT em uma coleo, como se ela fosse
uma tabela. sso pode ser muito til se voc precisar filtrar registros de uma coleo ou executar loops
constantes no mesmo cursor, por exemplo. Mas para realizar uma operao como essa, voc precisa:
1. Criar um objeto armazenado de banco de dados que contenha as colunas da coleo;
2. Criar um uma tipo Gtype7 armazenado no banco de dados que seja uma tabela do objeto
criado;
A criao de um objeto como esse pode se parecer muito com a definio de tipo registro ( redord),
como no exemplo abaixo:
CREATE OR REPLACE T<PE ObjCliente AS OBTECT
(cli_in_codigo INTEGER=
cli_st_nome 7ARC)AR4(46)=
cli_ch_novo C)AR(5)
)
1
Estando o objeto criado, voc deve criar um tipo tabela desse objeto. O comando para isso quase
igual a definio de um tipo tabela dentro de um bloco. A nica diferena o uso da palavra CREATE (ou
CREATE OR REPLACE). Veja:
CREATE OR REPLACE T<PE TCliente IS TABLE OF ObjCliente;
1
Por fim, dentro do seu bloco, voc s precisa declarar uma varivel do tipo criado e us-la como as
colees que j vimos at aqui:
DECLARE
-- Declarao de variveis
Elaborado por Josinei Barbosa da Silva
Pgina 95 de 154
Treinamento
cCli TCliente;
BEGIN
NULL;
END;
1
Depois que voc carregar os dados de sua coleo, voc pode executar uma declarao SELECT
nela. Para que PL/SQL visualizar a coleo como uma tabela, necessrio moldar a coleo como sendo
do seu tipo, atravs da funo CAST e depois, moldar resultado desse CAST como uma tabela. Veja o
exemplo abaixo:
-- Imprimir total de clientes novos
SELECT COUNT(*)
INTO vCount
FROM TABLE(CAST(cCli AS TCliente))
Onde:
vount uma varivel do tipo inteira previamente declarada;
*S! a funo que informa ao PL/SQL que cCli deve ser visto como um tipo Tcliente;
e !*BLE faz com que a PL/SQL veja a varivel do tipo TCliente, como uma tabela.
C53D'DO+ Quando usamos um tipo tabela aninhada armazenado para manipular colees, nos
deparamos com um problema de inicializao da coleo. No basta inicializar a coleo usando o
construtor do seu tipo, como vimos na seo de tabelas aninhadas. Alm disso, a cada entrada estendida,
necessrio inicializar a entrada com valores nulos antes de manipul-la, usando uma varivel do mesmo
tipo do elemento da coleo ou usando o prprio tipo para passar os valores nulos, como no exemplo
abaixo:
cC/i(5):3 ObjCliente(NULL=NULL=NULL);
Nesse exemplo, a entrada 1 da coleo cliente inicializada com nulo para cada um dos seus trs
campos, usando para isso o objeto ObjCliente, usado para definir o tipo de cada elemento do tipo
TCliente, que por sua vez foi usado para declarar a varivel cCli.
Para entendermos melhor, vejamos o seguinte exemplo:
1. Criao do objeto armazenado:
CREATE OR REPLACE T<PE ObjCliente AS OBTECT
(Codigo INTEGER=
Nome 7ARC)AR4(46)=
Novo C)AR(5)
)
1
2. Criao do tipo armazenado, sendo cada elemento do tipo do objeto criado no passoa 1:
CREATE OR REPLACE T<PE TCliente IS TABLE OF ObjCliente;
1
3. Criao do bloco que recupera todos os clientes, atravs de um cursor, e inclui cada cliente
retornado na coleo, alimentando o cdigo do cliente, nome do cliente e, com base na data de
Elaborado por Josinei Barbosa da Silva
Pgina 96 de 154
Treinamento
incluso, se o cliente novo ou no. Aps alimentar a coleo, imprimimos cada um dos clientes
informando se um cliente novo ou antigo. Por fim, executamos uma declarao SELECT para imprimir
a quantidade de clientes novos e outra para imprimir a quantidade de clientes antigos:
DECLARE
-- Declarao de cursores
CURSOR cs_cliente IS
SELECT c.*
FROM cliente c;

-- Declarao de variveis
cCli TCliente;
vIndice PLSFINTEGER :3 6;
vCount PLSFINTEGER :3 6;
BEGIN
-- Inicializa a coleo criando uma entrada vazia
cCli :3 TCliente();
-- Adiciona uma entrada na coleo para cada representante do cadastro
FOR csc IN cs_cliente LOOP
-- Estender a coleo em 1 entrada
cCli.EXTEND;
-- Definir valor do ndice
vIndice :3 cCli.LAST;

-- Inicializar a entrada estendida
cCli(vIndice):3 ObjCliente(NULL=NULL=NULL);

-- Atualizar os dados da entrada
cCli(vIndice).Codigo :3 csc.cli_in_codigo;
cCli(vIndice).Nome :3 csc.cli_st_nome;
IF csc.cli_dt_inclusao BET(EEN add_months(trunc(S<SDATE)=G54) AND S<SDATE T)EN
cCli(vIndice).Novo :3 'S';
ELSE
cCli(vIndice).Novo :3 'N';
END IF;

END LOOP;
-- Imprimir cdigo e nome de cada cliente informando se novo ou antigo
FOR i IN 5..vIndice LOOP
CASE cCli(i).Novo
()EN 'S' T)EN
dbms_output.put_line('C/int '##cCli(i).Nome##' ('##cCli(i).Codigo##') O No.oJ');
()EN 'N' T)EN
dbms_output.put_line('C/int '##cCli(i).Nome##' ('##cCli(i).Codigo##') O AntiLoJ');
END CASE;
END LOOP;
-- Quebrar linha
dbms_output.put_line('');

-- Imprimir total de clientes novos
SELECT COUNT(*)
INTO vCount
Elaborado por Josinei Barbosa da Silva
Pgina 97 de 154
Treinamento
FROM TABLE(CAST(cCli AS TCliente)) cn
()ERE cn.novo 3 'S';

dbms_output.put_line('Tot$/ % C/ints No.os2 '##vCount);

-- Imprimir total de clientes antigos
SELECT COUNT(*)
INTO vCount
FROM TABLE(CAST(cCli AS TCliente)) cn
()ERE cn.novo 3 'N';

dbms_output.put_line('Tot$/ % C/ints AntiLos2 '##vCount);
END;
1
O uso desse recurso pode representar ganhos considerveis em aplicaes crticas, pois os dados
so recuperados uma nica vez e pode ser utilizado em memria quantas vezes for necessrio, alm de
possibilitar uma manipulao pontual de cada registro atravs dos mtodos de tabela, j estudados.
Criando um cursor ex%l=cito a %artir de uma coleo
Outra maneira de utilizar uma coleo em uma instruo SQL, atribuindo seu retorno para uma
varivel do tipo REF CURSOR e manipul-la como um curso explcito.
Para isso basta definirmos um tipo REF CURSOR e declararmos uma varivel desse tipo. Depois s
abrir o cursor referenciando a varivel definida, usando para isso a declarao SELECT feita na coleo.
O exemplo abaixo semelhante ao anterior, porm, aps alimentarmos a coleo, abrimos um cursor
baseado nessa coleo restringindo seu retorno aos clientes novos. Em seguido iniciamos um LOOP no
cursor e imprimimos cada um dos clientes na tela.
DECLARE
/******** Declarao de cursores ********/
CURSOR cs_cliente IS
SELECT c.*
FROM cliente c;

/******** Definio de tipos ********/
T<PE TCursor IS REF CURSOR;
T<PE TRegistroCliente IS RECORD(
Codigo INTEGER=
Nome 7ARC)AR4(46)=
Novo C)AR(5)
);

/******** Declarao de variveis ********/
-- variveis de cursores
cs_ClienteNovo TCursor;
-- colees
cCli TCliente;
-- registros
rCliente TRegistroCliente;
-- esclares
vIndice PLSFINTEGER :3 6;
BEGIN
-- Inicializa a coleo criando uma entrada vazia
cCli :3 TCliente();
Elaborado por Josinei Barbosa da Silva
Pgina 98 de 154
Treinamento

-- Adiciona uma entrada na coleo para cada representante do cadastro
FOR csc IN cs_cliente LOOP
-- Estender a coleo em 1 entrada
cCli.EXTEND;
-- Definir valor do ndice
vIndice :3 cCli.LAST;

-- Inicializar a entrada estendida
cCli(vIndice):3 ObjCliente(NULL=NULL=NULL);

-- Atualizar os dados da entrada
cCli(vIndice).Codigo :3 csc.cli_in_codigo;
cCli(vIndice).Nome :3 csc.cli_st_nome;
IF csc.cli_dt_inclusao BET(EEN add_months(trunc(S<SDATE)=G54) AND S<SDATE T)EN
cCli(vIndice).Novo :3 'S';
ELSE
cCli(vIndice).Novo :3 'N';
END IF;
END LOOP;
-- Abri o cursor com uma declarao SELECT na coleo cCli
OPEN cs_ClienteNovo FOR SELECT Codigo=Nome=Novo
FROM TABLE(CAST(cCli AS TCliente)) cn
()ERE cn.novo 3 'S';

-- Imprimir cdigo e nome de cada cliente novo
LOOP
-- Recupera registro do cursor aberto a partir da coleo
FETC) cs_ClienteNovo INTO rCliente;
-- Sai do loop quando no encontrar registro no cursor
EXIT ()EN cs_ClienteNovo;NOTFOUND;

dbms_output.put_line('C/int '##rCliente.Nome##
' ('##rCliente.Codigo##') O No.oJ'
);
END LOOP;
-- Fecha cursor
CLOSE cs_ClienteNovo;
END;
1
38O9T'4T&+ Tipos de colees locais no so permitidos em instrues SQL, apenas tipos
armazenados.
O bulH bindin*
O bulk binding da PL/SQL um recurso que surgiu na verso Oracle8i. O bulk binding
permite codificar as declaraes SQL que operam em todas as entradas de uma coleo, sem ter de fazer o
LOOP em toda a coleo usando o cdigo PL/SQL. Vrios dos exemplos dados at aqui, usaram um LOOP
FOR de cursor para carregar os dados de uma tabela de banco de dados para uma tabela ou array da
PL/SQL. A mudana da SQL (FETCH) para a PL/SQL (para adicionar os dados ao array) chamada de
Elaborado por Josinei Barbosa da Silva
Pgina 99 de 154
Treinamento
mudan1a de conte0to e consome muita overhead. Voc pode usar o recurso de bulk binding para evitar
grande parte daquela overhead.
Duas novas palavras-chave suportam o binding: BULK COLLECT e FORALL.,
BULK COLLECT usada com as declaraes SELECT para colocar todos os dados em uma coleo.
FORALL usada com as declaraes INSERT, UPDATE e DELETE para executar aquelas declaraes
uma vez para cada elemento de uma coleo.
5sando BULH COLLECT
Voc pode usar as palavras-chave BULK COLLECT para ter os resultados de uma declarao
SELECT colocados diretamente em uma coleo. Voc pode usar BULK COLLECT com as declaraes
SELECT INTO e tambm com as declaraes FETCH. Por exemplo, se RepCodigos e RepNomes fossem
ambas tabelas aninhadas, voc emitiria a seguinte declarao SELECT para gerar nelas uma entrada para
cada representante existente na tabela representante:
SELECT rep_in_codigo= rep_st_nome
BULH COLLECT INTO RepCodigos= RepNomes
FROM representante;
Se voc tivesse um cursor chamado cs_representante que retornasse os mesmos dados, voc
poderia escrever BULK COLLECT na declarao FETCH da seguinte maneira:
OPEN cs_representante;
FETC) cs_representante BULH COLLECT INTO RepCodigos= RepNomes;
CLOSE cs_representante;
At o Oracle 8i, por algum motivo, a Oracle no permitia que voc usasse BULK COLLECT em uma
coleo de registros. Assim sendo, se voc selecionasse dez colunas, precisaria declarar dez colees,
uma para cada coluna.
A partir da verso 9r2 do Oracle Database, isso mudou, e passou a ser aceito o uso de colees de
registro. Dessa forma, se tivssemos uma varivel chamada cRep que fosse de um tipo registro que
contivesse uma coluna para cdigo e outra para nome, poderamos reescrever nossos dois exemplos
acima, como abaixo:
Na declarao SELECT:
SELECT rep_in_codigo= rep_st_nome
BULH COLLECT INTO cRep
FROM representante;
No FETCH do cursor:
OPEN cs_representante;
FETC) cs_representante BULH COLLECT INTO cRep;
CLOSE cs_representante;
Para exemplificar melhor, vamos reescrever o exemplo que classifica o cliente como novo ou antigo:
DECLARE
-- Declarao de cursores
CURSOR cs_cliente IS
SELECT ObjCliente(
c.cli_in_codigo=
c.cli_st_nome=
Elaborado por Josinei Barbosa da Silva
Pgina 100 de 154
Treinamento
(CASE
()EN (c.cli_dt_inclusao BET(EEN add_months(trunc(S<SDATE)=G54)
AND S<SDATE) T)EN 'S'
ELSE 'N'
END
)
)
FROM cliente c;

-- Declarao de variveis
cCli TCliente;
vCount PLSFINTEGER :3 6;
BEGIN
-- Inicializa a coleo criando uma entrada vazia
cCli :3 TCliente();

OPEN cs_cliente;
FETC) cs_cliente BULH COLLECT INTO cCli;
CLOSE cs_cliente;

-- Imprimir cdigo e nome de cada cliente informando se novo ou antigo
FOR i IN 5..cCli.COUNT LOOP
CASE cCli(i).Novo
()EN 'S' T)EN
dbms_output.put_line('C/int '##cCli(i).Nome##' ('##cCli(i).Codigo##') O
No.oJ');
()EN 'N' T)EN
dbms_output.put_line('C/int '##cCli(i).Nome##' ('##cCli(i).Codigo##') O
AntiLoJ');
END CASE;
END LOOP;
-- Quebrar linha
dbms_output.put_line('');

-- Imprimir total de clientes novos
SELECT COUNT(*)
INTO vCount
FROM TABLE(CAST(cCli AS TCliente)) cn
()ERE cn.novo 3 'S';

dbms_output.put_line('Tot$/ % C/ints No.os2 '##vCount);

-- Imprimir total de clientes antigos
SELECT COUNT(*)
INTO vCount
FROM TABLE(CAST(cCli AS TCliente)) cn
()ERE cn.novo 3 'N';

dbms_output.put_line('Tot$/ % C/ints AntiLos2 '##vCount);
END;
1
Este bloco PL/SQL apresenta apenas duas alteraes em relao ao bloco que usamos para
exemplificar o uso de declarao SELECT com colees:
Elaborado por Josinei Barbosa da Silva
Pgina 101 de 154
Treinamento
1. A declarao SELECT do cursor cs_cliente, com o uso da instruo CASE, j retorna se o
cliente novo ou no e as colunas retornadas na declarao so moldadas para o objeto ObjCliente
(uma espcie de CAST);
2. O cdigo que executava um LOOP no cursor e atribua os dados de cada cliente para a
coleo, foi substitudo por trs linhas que:
a) Abre o cursor (OPEN cs_cliente);
b) Alimenta a coleo com todos os dados do cursor (FETCH cs_cliente BULK
COLLECT INTO cCli);
c) Fecha o cursor (CLOSE cs_cliente).
Desta forma, ganhamos em performance e deixamos o cdigo mais enxuto.
5sando (O9'!!
A palavra-chave FORALL permite que voc baseie uma declarao DML (Data Manipulation
Language), ou seja, INSERT, UPDATE ou DELETE, no contedo de uma coleo. Quando FORALL usada,
a declarao executada uma vez para cada entrada da coleo, mas apenas uma mudana de contexto
feita da PL/SQL para a SQL. O desempenho resultante muito mais rpido do que aquilo que voc teria se
codificasse um LOOP na PL/SQL.
Para exemplificar o uso do FORALL, vamos alterar o exemplo do BULK COLLECT para passar o nome
de todos os clientes para maisculas:
DECLARE
-- Declarao de cursores
CURSOR cs_cliente IS
SELECT c.cli_in_codigo=
c.cli_st_nome
FROM cliente c;

-- Definio de tipo
T<PE TClienteID IS TABLE OF cliente.cli_in_codigo;T<PE;
T<PE TClienteNome IS TABLE OF cliente.cli_st_nome;T<PE;
-- Declarao de variveis
cCliID TClienteID;
cCliNome TClienteNome;
vCount PLSFINTEGER :3 6;
BEGIN
-- Carregar colees com dados do cursor
OPEN cs_cliente;
FETC) cs_cliente BULH COLLECT INTO cCliID= cCliNome;
CLOSE cs_cliente;

-- Alterar todos dos nomes da tabela de cliente para maiscula
FORALL x IN cCliID.FIRST..cCliID.LAST
UPDATE cliente
SET cli_st_nome 3 u,,&(cCliNome(x))
()ERE cli_in_codigo 3 cCliID(x);

-- Capturar quantidade de linhas atualizadas
vCount :3 SIL;RO(COUNT;

-- Informar quantidade de registros atualizados
Elaborado por Josinei Barbosa da Silva
Pgina 102 de 154
Treinamento
dbms_output.put_line(vCount##' &List&o(s) $tu$/iE$%o(s)'');

-- Efetivar alteraes no banco
COMMIT;
END;
1
Observe que neste exemplo, definimos duas colees cujos tipos esto definidos no prprio bloco, ao
invs de estarem armazenados no banco de dados. Uma coleo para conter o cdigo do cliente e a outra
para conter o nome. sso necessrio porque na declarao UPDATE do recurso FORALL, no conseguimos
referenciar o campo de um registro usado num bulk binding. Logo no conseguiramos utilizar o campo
codigo do objeto ObjCliente usado para definir os elementos do tipo TCliente (como vnhamos
fazendo at aqui).
Se voc tentar referenciar um campo de registro ao utilizar FORALL, receber o seguinte erro de
retorno:
PLSG6698N: restrio de implementao: no possvel fazer referncia a campos
da tabela de registros BULK InGBIND
Tratamento de excees %ara as colees
Algumas excees da PL/SQL esto relacionadas diretamente s colees. Elas esto listadas na
tabelas abaixo:
Tabela 7: E1ce29es relacionadas Ds cole29es
Exceo !ausa
COLLECTION_IS_NULL Voc tentou usar a coleo antes de incializ-la com sua
funo construtora.
NO_DATA_FOUND Voc tentou acessar o valor de uma entrada em uma coleo e
aquela entrada no existe.
SUBSCRIPT_BEYOND_COUNT Voc usou um subscript (o ndice) que excede o nmero de
elementos atuais da coleo.
SUBSCRIPT_OUTSIDE_LIMIT Voc usou um subscript (o ndice) com um varray que era maior
do que o mximo suportado pela declarao de tipo do array.
VALUE_ERROR Voc usou um subscript (o ndice) que no pode ser convertido
para um inteiro.
Ao escrever cdigo que lida com colees, voc pode detectar essas excees ou gravar cdigo para
evit-las. Voc pode evitar NO_DATA_FOUND, por exemplo, testando a validade de cada entrada com o
mtodo e0ists antes de tentar acessar o valor da entrada. O seguinte trecho de cdigo mostra como isso
feito:
-- Se o elemento 10 existe
IF cCli.EXISTS(56) T)EN
...
...
-- Se o elemento 10 no existe
ELSE
...
...
END IF;
Voc pode evitar os erros de subscript com cdigo cuidadoso. Se voc est trabalhando com
Elaborado por Josinei Barbosa da Silva
Pgina 103 de 154
Treinamento
VARRAY, saiba antes o nmero de elementos que voc declarou para aquele VARRAY conter. Se voc est
trabalhando com uma tabela aninhada, e no tem mais certeza do tamanho, use o mtodo count para
verificar o tamanho da tabela.
Elaborado por Josinei Barbosa da Silva
Pgina 104 de 154
Treinamento
&xerc=cios ro%ostos
1. Adicione a coluna cli_ch_novo na tabela cliente, usando o seguinte comando:
ALTER TABLE cliente
ADD (cli_ch_novo C)AR(5) DEFAULT 'S' NOT NULL
CONSTRAINT ck_cli_ch_novo C)ECH(cli_ch_novo IN('S'='N'))
);
Estando a tabela alterada, edite o pacote pck_cliente e crie um procedimento pblico, que:
Recupere os dados dos clientes atravs de um cursor;
Monte uma coleo (ou quantas achar necessrio) com os dados do cliente e mais
uma coluna que identifique se o cliente novo caso a data de incluso no tiver mais
do que um ano ou antigo, se a data de incluso tive mais do que um ano;
Atualize o cadastro de cada cliente da coleo, informando 'S' na coluna
cli_ch_novo quando o cliente for novo e 'N', quando o cliente for antigo.
2. Na package pck_representante, crie uma funo pblica que:
Receba como argumento o cdigo do representante, a data inicial do perodo e a data
final;
Alimente uma coleo com o total de cada venda realizada pelo representante no perodo
informado (tabela nota_fiscal_venda) calculando uma coluna extra com a comisso do
representante para cada nota fiscal;
Totalize as comisses do representante e retorne esse total.
A comisso deve ser 5% do valor total da nota, mais:
1% se o cliente for novo;
1% se a venda for vista ou 0,5% se a venda for em 3 vezes sem juros.
3. Adicione uma funo na package pck_produto que receba o cdigo do produto e retorne a
quantidade de vezes que foi praticado um valor menor do que o preo de vendas cadastrado. Para
isso, carregue todas as vendas do produto em uma coleo, registrando para cada elemento da
coleo o valor unitrio praticado e o valor de vendas cadastrado. Execute uma declarao SELECT
nessa coleo fazendo a contagem de vezes que o valor unitrio praticado maior do que o valor
de venda cadastrado. Lembre-se que o valor unitrio praticado est na tabela
item_nota_fiscal_venda e o valor de venda cadastrado est na tabela preco_produto, que
contm os preos de venda e de compra.
Elaborado por Josinei Barbosa da Silva
Pgina 105 de 154
Treinamento
05 T!icos a"anados# PL/SQL
1. !"S#!+
S#! Din0mico
Stored Procedure com transao aut?noma
(uno "I"ELI#ED
UTL_FLE: Escrita e leitura de arquivos no servidor
Elaborado por Josinei Barbosa da Silva
Pgina 106 de 154
Treinamento
S#! Din0mico
SQL Dinmico a SQL ou PL/SQL que gerada por um programa quando ele executado. Voc usa
a SQL dinmica quando precisa escrever software genrico. Por exemplo, se voc desenvolver um gerador
de relatrios no tem como saber com antecedncia quais so os relatrios que as pessoas criaro usando
esse gerador. Neste caso, voc precisa tornar o seu cdigo flexvel e genrico o suficiente para permitir que
os usurios executem qualquer consulta que queiram.
O SQL dinmico um recurso para fornecer a flexibilidade no desenvolvimento de cdigo PL/SQ e
SQLs genricos. Esse cdigo gerado pelo software no runtime e depois executado.
'T&4IJO+ SQL dinmico deve ser utilizado com responsabilidade, pois ele prejudicial para a
performance do banco de dados. O SQL dinmico no compartilhado na memria do banco de dados.
Nos exemplos que vimos at aqui, trabalhamos com SQL esttico (tambm chamado de nativo), onde
necessrio saber com antecedncia como deve ser seu SQL.
Exemplo: Se quisermos que nossa package pck_cliente tenha uma funo que retorne a mdia geral
de compra de clientes, permitindo que o usurio decida se quer a mdia de:
um ramo de atividade
uma cidade
de um ramo em uma cidade
ou geral,
Teramos que criar uma cdigo como o que segue abaixo:
G&a especifica14o da package7
$$ %uno &ue retorna a mdia de compra '(era)
$$ por ramo de ati*idade) cidade ou ramo + cidade,
FUNCTION GetMediaCompraMensal
(pRamo cliente.cli_st_ramoatividade;T<PE
=pCidade cliente.cli_st_cidade;T<PE)
RETURN NUMBER;
G&o corpo da package7
$$ %uno &ue retorna a media de compra '(era)
$$ por ramo de ati*idade) cidade ou ramo + cidade,
FUNCTION GetMediaCompraMensal
(pRamo cliente.cli_st_ramoatividade;T<PE
=pCidade cliente.cli_st_cidade;T<PE)
RETURN NUMBER IS
vlResult cliente.cli_vl_mediacomprasmensal;T<PE 23 6;
BEGIN

-- Se for por ramo de atividade e cidade
IF (pRamo IS NOT NULL) AND (pCidade IS NOT NULL) T)EN
SELECT A7G(c.cli_vl_mediacomprasmensal)
INTO vlResult
FROM cliente c
Elaborado por Josinei Barbosa da Silva
Pgina 107 de 154
Treinamento
()ERE c.cli_st_ramoatividade 3 pRamo
AND c.cli_st_cidade 3 pCidade;

-- Se for apenas por ramo de atividade
ELSIF (pRamo IS NOT NULL) AND (pCidade IS NULL) T)EN
SELECT A7G(c.cli_vl_mediacomprasmensal)
INTO vlResult
FROM cliente c
()ERE c.cli_st_ramoatividade 3 pRamo;

-- Se for apenas por cidade
ELSIF (pRamo IS NULL) AND (pCidade IS NOT NULL) T)EN
SELECT A7G(c.cli_vl_mediacomprasmensal)
INTO vlResult
FROM cliente c
()ERE c.cli_st_cidade 3 pCidade;

-- Se for geral
ELSIF (pRamo IS NULL) AND (pCidade IS NULL) T)EN
SELECT A7G(c.cli_vl_mediacomprasmensal)
INTO vlResult
FROM cliente c;

END IF;
RETURN(vlResult);
EXCEPTION
()EN no_data_found T)EN
RETURN(6);
()EN OT)ERS T)EN
raise_application_error
(G46566= 'N*o +oi ,ossi./ &cu,&$& !O%i$ % co!,&$ ,$&$2 '##
chr(58)##'R$!o2 '##pRamo##
chr(58)##'Ci%$%2 '##pCidade##
chr(58)##
chr(58)##'E&&o2 '##s0/&&!);

END GetMediaCompraMensal;
A soluo acima atende a necessidade que descrevemos, conforme podemos conferir com a
instruo SQL abaixo:
SELECT c.cli_in_codigo "Cod. Cliente"
=c.cli_st_nome "Nome Cliente"
=c.cli_vl_mediacomprasmensal "Media Mensal Compra"
=c.cli_st_ramoatividade "Ramo Atividade"
=pck_cliente.GetMediaCompraMensal
(c.cli_st_ramoatividade
=NULL) "Media Ramo"
=c.cli_st_cidade "Cidade"
=pck_cliente.GetMediaCompraMensal
(NULL
= c.cli_st_cidade) "Media Cidade"
=pck_cliente.GetMediaCompraMensal
(c.cli_st_ramoatividade
Elaborado por Josinei Barbosa da Silva
Pgina 108 de 154
Treinamento
=c.cli_st_cidade) "Media Ramo/Cidade"
FROM cliente c;
Mas observe na funo que, nas quatro possibilidades de retorno da funo, repetimos a mesma SQL
trocando apenas suas restries (WHERE . AND).
A mesma funo poderia ser reescrita utilizando SQL dinmico com um SQL genrico, como vemos
abaixo:
FUNCTION GetMediaCompraMensal
(pRamo cliente.cli_st_ramoatividade;T<PE
=pCidade cliente.cli_st_cidade;T<PE)
RETURN NUMBER IS
vlResult cliente.cli_vl_mediacomprasmensal;T<PE 23 6;
vlSQL LONG;
vlWhereOrAnd 7ARC)AR4(N) 23 '()ERE ';
BEGIN

vlSQL 23 'SELECT A7G(c'c/iF./F!%i$co!,&$s!ns$/) '##
'FROM c/int c ';

-- iltrar !amo
IF (pRamo IS NOT NULL) T)EN
vlSQL 23 vlSQL ##
'()ERE c'c/iFstF&$!o$ti.i%$% 3 '''##pRamo##''' ';

vlWhereOrAnd 23 'AND ';
END IF;

-- iltrar "idade
IF (pCidade IS NOT NULL) T)EN
vlSQL 23 vlSQL ##
vlWhereOrAnd##'c'c/iFstFci%$% 3 '''##pCidade##''' ';
END IF;
EXECUTE IMMEDIATE vlSQL
INTO vlResult;

RETURN(vlResult);
EXCEPTION
()EN no_data_found T)EN
RETURN(6);
()EN OT)ERS T)EN
raise_application_error
(G46566= 'N*o +oi ,ossi./ &cu,&$& !O%i$ % co!,&$ ,$&$2 '##
chr(58)##'R$!o2 '##pRamo##
chr(58)##'Ci%$%2 '##pCidade##
chr(58)##
chr(58)##'E&&o2 '##s0/&&!);

END GetMediaCompraMensal;
No exemplo acima, ao invs de concatenarmos os parmetros p"amo e pidade na SQL,
poderamos ter usado a clusula USING do comando EXECUTE IMMEDIATE para informar as variveis de
Elaborado por Josinei Barbosa da Silva
Pgina 109 de 154
Treinamento
ligao, mas para isso, seria preciso garantir que todas as variveis de ligao (bind variables) informadas
estariam presentes na SQL executada (em nosso exemplo, nem sempre isso aconteceria).
38O9T'4T&+ O 'EXECUTE MMEDATE' foi introduzido na verso 8i do Oracle Database. Nas
verses anteriores, a nica maneira de gerar SQL Dinmico era atravs do pacote DBMS_SQL e sua
utilizao no era das mais fceis.
S#! Din0mico em cursores
Tambm podemos criar cursores com SQL dinmico, concatenando valores ou usando variveis de
ligao, como no exemplo abaixo:
DECLARE
vlCursor S<SFREFCURSOR;
vlRamo cliente.cli_st_ramoatividade;T<PE :3 'SUPERMERCADO';
vlCliente cliente;RO(T<PE;
BEGIN
OPEN vlCursor FOR 'SELECT * '##
'FROM c/int c '##
'()ERE c'c/iFstF&$!o$ti.i%$% 3 25' USING vlRamo;

LOOP
FETC) vlCursor INTO vlCliente;
EXIT ()EN vlCursor;NOTFOUND;

dbms_output.put_line('C/int '##vlCliente.cli_st_nome);
END LOOP;
CLOSE vlCursor;
END;
1
Stored rocedure com transao aut?noma
Pode existir situaes onde uma programa invoque outro programa e algo do segundo programa
precise de um COMMIT independente do que acontea no primeiro. Por sua vez, o primeiro programa no
pode ser afetado de forma alguma pelo COMMIT executado no segundo.
Pelo que vimos at o momento, isso no seria possvel, pois um COMMIT no segundo programa
tambm valeria para tudo que foi executado no primeiro at aquele momento.
Resumindo, o que precisamos que o segundo programa tenha uma transao independente
(autnoma). Para que isso seja possvel, o Oracle disponibiliza o PRAGMA AUTONOMOUSFTRANSACTION.
Com esse recurso, voc poder implementar programas PL/SQL que possuem transao independente, ou
seja, qualquer COMMIT ou ROLLBACK que ocorrer dentro desse programa, s valer para ele.
'T&4IJO+ Use o PRAGMA AUTONOMOUS_RANSACTION com responsabilidade, pois ele pode
causar comportamentos indesejados no seu sistema e dificultar a manuteno.
Vejamos um exemplo simples de como utilizar esse recurso:
Crie as seguintes tabelas:
Elaborado por Josinei Barbosa da Silva
Pgina 110 de 154
Treinamento
CREATE TABLE atualizacao_resumo_vendas
(arv_dt_atualizacao DATE NOT NULL
=arv_st_usuario 7ARC)AR4(86) NOT NULL
=arv_st_conclusao 7ARC)AR4(56) DEFAULT 'EXECUTANDO' NOT NULL
CONSTRAINT ck_arv_st_conclusao C)ECH(arv_st_conclusao
IN('EXECUTANDO'='OH'='FAL)OU'))
)
1
CREATE TABLE temp_atualizacao_resumo
(tar_dt_atualizacao DATE NOT NULL
=tar_st_usuario 7ARC)AR4(86) NOT NULL
=tar_st_conclusao 7ARC)AR4(56) DEFAULT 'EXECUTANDO' NOT NULL
CONSTRAINT ck_tar_st_conclusao C)ECH(tar_st_conclusao
IN('EXECUTANDO'='OH'='FAL)OU'))
);
mplemente a seguinte package:
CREATE OR REPLACE PACHAGE pck_vendas IS
-- #ut$or % &'SI()IS
-- "reated % *+/*/,-** *.%+/%.0
-- 1urpose % pacote para manipulao de vendas

/* 1rocedure para atuali2ar o resumo mensal de vendas */
PROCEDURE AtualizaResumoMensal;
END pck_vendas;
1
CREATE OR REPLACE PACHAGE BOD< pck_vendas is
/* !egistrar log de atuali2ao do resumo mensal de vendas */
PROCEDURE RegistrarAtualizacaoResumo
(pSituacaoConclusao atualizacao_resumo_vendas.arv_st_conclusao;T<PE) IS
PRAGMA AUTONOMOUSFTRANSACTION;
BEGIN
BEGIN
INSERT INTO
atualizacao_resumo_vendas(arv_dt_atualizacao=arv_st_usuario=arv_st_conclusao)
7ALUES(S<SDATE=USER=pSituacaoConclusao);

COMMIT;
EXCEPTION
()EN dup_val_on_index T)EN
NULL;
()EN OT)ERS T)EN
raise_application_error
(G46566= 'N*o +oi ,oss-./ &List&$& $ $tu$/iE$V*o %o &su!o !ns$/
% .n%$s'##
chr(58)##'E&&o2 '##SILERRM);
END;

Elaborado por Josinei Barbosa da Silva
Pgina 111 de 154
Treinamento
END RegistrarAtualizacaoResumo;

/* 1rocedure para atuali2ar o resumo mensal de vendas */
PROCEDURE AtualizaResumoMensal IS

-- 3eclarao de cursores
CURSOR cs_vendas IS
SELECT to_char(n.nfv_dt_emissao='QQQQ') ano=
to_char(n.nfv_dt_emissao='!!') mes=
i.pro_in_codigo=
n.cli_in_codigo=
n.rep_in_codigo=
SUM(i.infv_vl_total) vl_total=
COUNT(i.infv_in_numero) numerovendas=
SUM(i.infv_qt_faturada) qt_faturada
FROM nota_fiscal_venda n=
item_nota_fiscal_venda i
()ERE n.nfv_in_numero 3 i.nfv_in_numero
GROUP B< to_char(n.nfv_dt_emissao='QQQQ')=
to_char(n.nfv_dt_emissao='!!')=
i.pro_in_codigo=
n.cli_in_codigo=
n.rep_in_codigo;
BEGIN

RegistrarAtualizacaoResumo(pSituacaoConclusao 3A 'EXECUTANDO');

-- Inicia Loop nos registros de venda
FOR csv IN cs_vendas LOOP
BEGIN
INSERT INTO
resumo_mensal_venda(rmv_in_ano=rmv_in_mes=pro_in_codigo=cli_in_codigo=rep_in_cod
igo=rmv_vl_total=rmv_in_numerovendas=rmv_qt_vendida)
7ALUES(csv.ano=csv.mes=csv.pro_in_codigo=csv.cli_in_codigo=csv.rep_in_
codigo=csv.vl_total=csv.numerovendas=csv.qt_faturada);
EXCEPTION
()EN dup_val_on_index T)EN
UPDATE resumo_mensal_venda r
SET r.rmv_vl_total 3 csv.vl_total=
r.rmv_in_numerovendas 3 csv.numerovendas=
r.rmv_qt_vendida 3 csv.qt_faturada
()ERE r.rmv_in_ano 3 csv.ano
AND r.rmv_in_mes 3 csv.mes
AND r.pro_in_codigo 3 csv.pro_in_codigo
AND r.cli_in_codigo 3 csv.cli_in_codigo
AND r.rep_in_codigo 3 csv.rep_in_codigo;
()EN OT)ERS T)EN
RegistrarAtualizacaoResumo(pSituacaoConclusao 3A 'FAL)OU');
ROLLBACH;
raise_application_error
(G46566='N*o +oi ,oss-./ $tu$/iE$& o sLuint &su!o !ns$/ %
.$n%$s2'##
chr(58)##'Ano''''''''''2 '##to_char(csv.ano) ##
chr(58)##'MWs''''''''''2 '##to_char(csv.mes) ##
chr(58)##'P&o%uto''''''2 '##to_char(csv.pro_in_codigo)##
chr(58)##'C/int''''''2 '##to_char(csv.cli_in_codigo)##
Elaborado por Josinei Barbosa da Silva
Pgina 112 de 154
Treinamento
chr(58)##'R,&snt$nt2 '##to_char(csv.rep_in_codigo)##
chr(58)##
chr(58)##'E&&o2 '##SILERRM);
END;
END LOOP;
RegistrarAtualizacaoResumo(pSituacaoConclusao 3A 'OH');

END AtualizaResumoMensal;
END pck_vendas;
1
Abra duas janelas SQL e:
1. Na primeira execute o seguinte bloco (4JO &K&C5T& CO883T):
BEGIN
pck_vendas.AtualizaResumoMensal;

INSERT INTO temp_atualizacao_resumo
(tar_dt_atualizacao
=tar_st_usuario
=tar_st_conclusao)
7ALUES
(S<SDATE
=USER
='OH');
END;
1
2. Em seguida, execute as seguintes consultas na mesma janela do bloco annimo:
SELECT * FROM atualizacao_resumo_vendas
1
SELECT * FROM temp_atualizacao_resumo
1
Todos os NSERTs realizados na operao sero vistos, pois como estamos na mesma
transao do bloco, tambm visualizamos o que ainda no sofreu COMMT, ou seja:
Na primeira consulta: Um registro com a situao 'EXECUTANDO' e outro com a
situao 'OK'. Esses registros foram includos pela procedure
pck_vendas.RegistrarAtualizacaoResumo, que possui transao
autnoma;
Na segunda consulta: Um registro com a situao 'OK', includo pelo INSERT do
nosso bloco annimo e que ainda no sofreu COMMIT;
3. Na segunda janela de SQL, execute as duas consultas novamente e veja que:
A primeira consulta exibe o mesmo resultado da outra janela SQL (registros
includos pela procedure com transao autnoma);
A segunda consulta no exibe o registro includo no bloco annimo (ainda sem
COMMIT);
4. Volte na janela do bloco annimo e execute o COMMIT;
Elaborado por Josinei Barbosa da Silva
Pgina 113 de 154
Treinamento
5. Na segunda janela de SQL, repita as duas consultas e veja que agora a segunda consulta
mostra o registro includo pelo bloco annimo (e finalmente submetido ao COMMIT);
Como pode ser observado, desde a execuo do bloco annimo, os registros da tabela
ATUALIZACAO_RESUMO_VENDAS, includos e submetidos ao COMMIT pela procedure
pck_vendas.RegistrarAtualizacaoResumo (que possui transao autnoma), eram visveis nas
duas janelas de SQL.
Em contrapartida, os registros da tabela TEMP_ATUALIZACAO_RESUMO, cujo INSERT acontecia no
bloco annimo, s passaram a ser visveis na outra janela de SQL aps o COMMIT;
38O9T'4T&+ Somente a partir da verso 9i do Oracle Database que stored proceures com
transao autnoma ganharam suporte para transaes entre bancos de dados distribudos.
Tabela (uno !"S#! 6PIPELINED7
Uma tabela funo PL/SQL, tambm chamada de fun14o pipelined, um recurso pouco conhecido do
Oracle 9i.
Funo pipelined um recurso PL/SQL para gerar registros em memria, como se fosse uma uma
tabela virtual. A grande vantagem dessa modalidade a habilidade de devolver linhas dinamicamente a
partir da gerao da mesma, ou seja, quando inclumos um registro nessa tabela virtual (atravs do
comando PIPE ROW), ele a envia de volta ao cliente, antes de gerar a prxima, ao contrrio de um array
PL/SQL, que constri todos os registros em batch antes de retorn-los ao cliente. sso quer dizer que o
recurso de memria pouco utilizado, pois no precisa "segurar a informao.
Dessa forma voc consegue eliminar a necessidade de armazenar dados que so apenas para
auxili-lo na recuperao de outros dados, minimizando o uso do recurso de memria.
Um uso comum para esse recurso para fazer carga de uma dimenso de tempo dentro de um
Datawarehouse, onde voc precisa gerar vrias linhas de referncias aos anos e isso no tem uma origem
exata.
Suponha que voc em seu ambiente de trabalho se depare com um problema onde voc precise
executar uma declarao SELECT que retorne o valor total de venda para cada ms de um determinado
perodo, por exemplo, de 01/2006 a 12/2006:
"er-odo .aor
01/2006 0
02/2006 80000
03/2006 20000
04/2006 50000
05/2006 60000
06/2006 15000
07/2006 100000
08/2006 35000
09/2006 40000
10/2006 70000
11/2006 0
12/2006 87000
Observe que nos meses de janeiro e novembro os valores so zerados, provavelmente porque no
foram realizadas vendas. Uma soluo possvel criar uma tabela na base de dados com apenas um
Elaborado por Josinei Barbosa da Silva
Pgina 114 de 154
Treinamento
campo, contendo todos os perodos (ms/ano) para relacionar com a tabela que contm o total de vendas.
Suponhamos que tivssemos uma tabela chamada perodo_resumo que contivesse todos os
perodos de que precisamos, como abaixo:
per/in/mes per/in/ano
01 2006
02 2006
03 2006
04 2006
05 2006
06 2006
07 2006
08 2006
09 2006
10 2006
11 2006
12 2006
Considerando que o total de vendas por perodo ser obtido pela tabela resumo_mensal_vendas,
teramos que relacionar essas duas tabelas da seguinte maneira para obtermos o resultado que desejamos.
SELECT p.per_in_mes=
p.per_in_ano=
SUM(r.rmv_vl_total) rmv_vl_total
FROM periodo_resumo p=
resumo_mensal_venda r
()ERE p.per_in_mes BET(EEN 5 AND 54
AND p.per_in_ano 3 466N
AND p.per_in_mes 3 r.rmv_in_mes(P)
AND p.per_in_ano 3 r.rmv_in_ano(P)
GROUP B< p.per_in_mes=
p.per_in_ano
ORDER B< p.per_in_mes=
p.per_in_ano;
O resultado esperado alcanado utilizando somente instrues SQL, mas precisamos manter dados
persistentes de perodo s para isso.
O Oracle um banco de dados Objeto-Relacional e, sendo assim, possui extenses para suporte de
classes e objetos sendo esse o recurso necessrio para a criao de uma "PL/SQL table function, ou seja,
uma funo em PL/SQL que retorne um conjunto de dados complexos que possa ser tratado como uma
tabela substituindo assim dados por um "objeto no persistente que seja acessvel na linguagem SQL.
Com esse recurso, podemos montar a "tabela de perodos apenas quando precisarmos utiliz-la, no
precisando armazenar esses dados.
8ontando uma 1uno PIPELINED
Se a funo retornar um conjunto de dados, primeiramente precisaremos criar no banco, essa
estrutura que ser retornada, ou seja, no basta criar a funo. Para trabalhar com o PIPELINED
necessrio:
1. Criar um objeto de banco de dados armazenado, contendo as colunas das quais voc
precisar;
Elaborado por Josinei Barbosa da Silva
Pgina 115 de 154
Treinamento
2. Criar uma coleo onde cada elemento do tipo do objeto criado no primeiro passo;
3. Criar uma funo do tipo PIPELINED que retorna a coleo criada no segundo passo.
Vamos usar o nosso exemplo como perodos para exemplificar esse processo:
1. Criao do objeto armazenado no banco para possibilitar registro de ms e ano:
CREATE OR REPLACE T<PE ObjPeriodo AS OBTECT (
mes INTEGER=
ano INTEGER
);
1
2. Criao da coleo de perodos (ms/ano):
CREATE OR REPLACE T<PE TPeriodo AS TABLE OF ObjPeriodo;
1
3. Criao da funo PIPELINED que retorna os perodos desejados:
CREATE OR REPLACE PACHAGE pck_util IS
--Declarao da sua funo
FUNCTION fnPeriodo(pDataInicial DATE= pDataFinal DATE)
RETURN TPeriodo PARALLELFENABLE PIPELINED;
END pck_util;
1
CREATE OR REPLACE PACHAGE BOD< pck_util IS
-- Funo que retorna coleo de perodo
FUNCTION fnPeriodo(pDataInicial DATE= pDataFinal DATE)
RETURN TPeriodo PARALLELFENABLE PIPELINED IS
-- Declarao de variveis
vQtdeMeses INTEGER :3 6;
vDataInicial DATE :3 pDataInicial;
vDataFinal DATE :3 pDataFinal;
vDataAtual DATE :3 add_months(pDataInicial=G5);
vResult ObjPeriodo :3 ObjPeriodo(NULL=NULL);
BEGIN
-- Acha a quantidade de meses entre as duas datas,
-- incluindo 1 para considerar o ltimo ms
vQtdeMeses :3 months_between(vDataFinal=vDataInicial);

-- Inicia uma loop que vai do ms/ano inicial at o ms/ano final
FOR i IN 5..vQtdeMeses LOOP
-- Incrementa um ms na data atual
vDataAtual :3 add_months(vDataAtual=5);

-- Defini os valores do registro atual
vResult.Mes :3 to_number(to_char(vDataAtual='MM'));
vResult.Ano :3 to_number(to_char(vDataAtual='<<<<'));

-- Adiciona registro na coleo
PIPE RO( (vResult);
END LOOP;

RETURN;
END fnPeriodo;
Elaborado por Josinei Barbosa da Silva
Pgina 116 de 154
Treinamento
END pck_util;
1
Neste exemplo, primeiramente, foi criado um tipo de objeto chamado ObjPeriodo que contm dois
atributos: ms e ano. Depois, criamos uma coleo chamada TPeriodo, que conter linhas do tipo
ObjPeriodo.
Aps criar o objeto e a coleo, foi criado um pacote chamado pck_util, que contm a funo
fnPeriodo, que como podemos observar, retorna um objeto TPeriodo e recebe como parmetros uma
data do perodo inicial e uma data do perodo final.
A clusula PIPELINED da funo o que permite a utilizao do comando PIPE ROW que o
responsvel por incluir o registro na coleo que ser retornada. PARALLEL_ENABLE permite que a funo
seja executada em sesses filhas de operaes paralelas, portanto, opcional mas necessria se voc
utiliza PARALLEL QUERY.
5tili>ando uma 1uno PIPELINED em uma declarao S#!
Para utilizar uma funo PIPELINED em uma instruo SQL to simples quanto usar as tabelas do
banco de dados. A nica diferena, que precisamos fazer um typecasting da funo, como no exemplo
abaixo:
SELECT *
FROM TABLE(pck_util.fnPeriodo(add_months(S<SDATE=G54)=S<SDATE))
Neste exemplo, temos como retorno os ltimos 12 perodos mensais:
Elaborado por Josinei Barbosa da Silva
Pgina 117 de 154
Treinamento
Figura 22: >esultado do uso de uma un23o PEPELEFE&
Mas como podemos utilizar esse recurso para resolver o nosso problema inicial, onde precisvamos
recuperar o valor total de venda para cada ms no perodo de 01/2006 a 12/2006?
A nossa declarao SELECT que dependia da tabela periodo_resumo, no precisa mais, pois
podemos reescrev-la da seguinte forma:
SELECT p.mes=
p.ano=
SUM(nvl(r.rmv_vl_total=6)) rmv_vl_total
FROM TABLE(pck_util.fnPeriodo(to_date('651651466N'='%%1!!1QQQQ')=
to_date('651541466N'='%%1!!1QQQQ')
)
) p=
resumo_mensal_venda r
()ERE p.mes BET(EEN 5 AND 54
AND p.ano 3 466N
AND p.mes 3 r.rmv_in_mes(P)
AND p.ano 3 r.rmv_in_ano(P)
GROUP B< p.mes=
p.ano
ORDER B< p.mes=
p.ano;
Ou se preferir:
SELECT p.mes=
p.ano=
(SELECT SUM(nvl(r.rmv_vl_total=6))
FROM resumo_mensal_venda r
()ERE p.mes 3 r.rmv_in_mes AND p.ano 3 r.rmv_in_ano
) rmv_vl_total
FROM TABLE(pck_util.fnPeriodo(to_date('651651466N'='%%1!!1QQQQ')=
to_date('651541466N'='%%1!!1QQQQ')
)
) p
UTLFFILE+ &scrita e leitura de arquivos no servidor
A linguagem PL/SQL em si no possui mecanismo para executar sada para arquivo ou tela.
Entretanto, a Oracle fornece alguns pacotes incorporados que permitem executar a E/S e que podem ser
chamados da PL/SQL.
O pacote UTL_FILE o mais conhecido e mais simples de se utilizar e veremos as seguir como
extrair informaes do banco de dados e gerar um arquivo com esses dados no sistema operacional do
servidor, bem como ler um arquivo do sistema operacional e manipular seus dados.
Existem dois pr-requisitos para usar o pacote UTL_FILE:
Voc deve ter privilgios de executar o pacote;
Seu administrador de banco de dados deve definir um parmetro de inicializao de banco de
dados chamado UTL_FILE_DIR ou criar um DIRECTORY no banco de dados;
Elaborado por Josinei Barbosa da Silva
Pgina 118 de 154
Treinamento
Usando UTL_FILE, o processo para leitura ou gravao de um arquivo o seguinte:
1. Declarar uma varivel de handle de arquivo para usar na identificao do arquivo quando
voc fizer chamadas para as diversas rotinas do UTL_FLE.
2. Declarar uma string do tipo VARCHAR2 para agir como um buffer para ler o arquivo uma
linha de cada vez;
3. Abrir o arquivo, especificando se far leitura ou escrita;
4. Manipular a linha, seja uma leitura ou uma escrita (existem sub-rotinas especficas de
UTL_FLE para cada caso);
5. Fechar o arquivo.
rocedimentos e 1unes de 5T!<(3!&
Na tabela 8 esto os procedimentos e funes que encontramos no pacote UTL_FILE.
Tabela 8: Procedimentos e un29es de G7LHFELE
"roocedure 0 %unction Descrio
FCLOSE Fecha um arquivo.
ExcVXs /.$nt$%$s
ExcV*o Dsc&iV*o
UTL_FILE.INVALID_FILEHANDLE 4oc5 passou um $andle de ar6uivo
6ue no representava um ar6uivo
a7erto8
UTL_FILE.WRITE_ERROR ' sistema operacional no pode
gravar no ar6uivo8
UTL_FILE.INTERNAL_ERROR 9m erro interno ocorreu8
FCLOSE_ALL Fecha todos os arquivos.
ExcVXs /.$nt$%$s
As mesmas da funo FCLOSE.
FFLUSH Descarrega todos os dados em 7uffer para serem gravados em
disco imediatamente.
ExcVXs /.$nt$%$s
ExcV*o Dsc&iV*o
UTL_FILE.INVALID_FILEHANDLE 4oc5 usou um $andle de ar6uivo
inv:lido8 1rovavelmente voc5
es6ueceu de a7rir o ar6uivo8
UTL_FILE.INVALID_OPERATION 4oc5 tentou gravar em um ar6uivo
6ue no estava a7erto para gravao
;modos < e #=8
UTL_FILE.WRITE_ERROR 'correu um erro de sistema
operacional> tal como um erro de
disco c$eio> ao tentar gravar em um
ar6uivo
UTL_FILE.INTERNAL_ERROR 'correu um erro interno8
FOPEN Abre um arquivo.
ExcVXs /.$nt$%$s
ExcV*o Dsc&iV*o
UTL_FILE.INVALID_PATH ' diret?rio no @ v:lido
UTL_FILE.INVALID_MODE 9m modo inv:lido foi especificado8
' modem open pode ser ! ;ler=> <
Elaborado por Josinei Barbosa da Silva
Pgina 119 de 154
Treinamento
"roocedure 0 %unction Descrio
;escrever= ou # ;para anexar a um
ar6uivo existente=8
UTL_FILE.INVALID_OPERATION ' ar6uivo no pode ser a7erto por
algum outro motivo> como por
exemplo> falta de permisso de
acesso do propriet:rio do softAare
do 'racle8
(a maioria das ve2es> @ pro7lema de
permisso de acesso no sistema
operacional8
UTL_FILE.INTERNAL_ERROR 9m erro interno ocorreu8
GET_LINE L uma linha de um arquivo e avana para a prxima.
ExcVXs /.$nt$%$s
ExcV*o Dsc&iV*o
UTL_FILE.INVALID_FILEHANDLE 4oc5 passou um $andle de ar6uivo
inv:lido8 1ossivelmente> voc5
es6ueceu de a7rir o ar6uivo
primeiro8
UTL_FILE.INVALID_OPERATION ' ar6uivo no est: a7erto para
leitura ;modo != ou existem
pro7lemas com as permissBes do
ar6uivo8
UTL_FILE.VALUE_ERROR ' 7uffer no @ suficientemente
longo para conter a lin$a 6ue est:
sendo lida do ar6uivo8 ' taman$o do
7uffer @ aumentado8
UTL_FILE.NO_DATA_FOUND ' final do ar6uivo foi atingido8
UTL_FILE.INTERNAL_ERROR 'correu um erro interno do sistema
9CL_IL)8
UTL_FILE.READ_ERROR 'correu um erro do sistema
operacional durante a leitura do
ar6uivo8
IS_OPEN Verifica se um arquivo est aberto.
NEW_LINE Grava um caractere neAline em um arquivo.
ExcVXs /.$nt$%$s
As mesmas da funo FFLUSH.
PUT Grava uma string de caracteres em um arquivo, mas no coloca
uma neAline depois dela.
ExcVXs /.$nt$%$s
As mesmas da funo FFLUSH.
PUT_LINE Grava uma linha em um arquivo.
ExcVXs /.$nt$%$s
As mesmas da funo FFLUSH.
PUTF Formata e grava sada. Essa uma imitao bruta do
procedimento printf() da linguagem C.
ExcVXs /.$nt$%$s
As mesmas da funo FFLUSH.
Elaborado por Josinei Barbosa da Silva
Pgina 120 de 154
Treinamento
Lerando um arquivo com in1ormaes extra=das do banco de dados
Vamos criar na package pck_vendas, uma procedure que recupera o cadastro de produto do banco
de dados e gerar um arquivo no servidor no formato CSV. Abaixo segue o cdigo da nova procedure:
/* 1rocedure 6ue exporta o cadastro de produtos para um ar6uivo "S4*/
PROCEDURE ExpProdutoCSV IS
vlFile utl_file.file_type;

CURSOR cs_produto IS
SELECT p.pro_in_codigo
=p.pro_st_nome
=p.pro_st_marca
=(SELECT pp.ppr_vl_unitario
FROM preco_produto pp
()ERE pp.pro_in_codigo 3 p.pro_in_codigo
AND pp.ppr_st_tipopreco 3 '7ENDAS'
) vl_preco_venda
=(SELECT pp.ppr_vl_unitario
FROM preco_produto pp
()ERE pp.pro_in_codigo 3 p.pro_in_codigo
AND pp.ppr_st_tipopreco 3 'COMPRAS'
) vl_preco_compra
FROM produto p;
BEGIN
-- #7rir ar6uivo
vlFile 23 utl_file.fopen('INTERFACE'='C$%P&o%G'##USER##''cs.'='(');

-- Dravar ca7eal$o
utl_file.put_line(vlFile
= 'YCo%iLoY='
##'YNo!Y='
##'YM$&c$Y='
##'YP&co % 7n%$Y='
##'YP&co % Co!,&$Y');

-- Dravar produtos no ar6uivo
FOR csp IN cs_produto LOOP
utl_file.put_line(vlFile
= 'Y'##csp.pro_in_codigo ##'Y='
##'Y'##csp.pro_st_nome ##'Y='
##'Y'##csp.pro_st_marca ##'Y='
##'Y'##csp.vl_preco_venda ##'Y='
##'Y'##csp.vl_preco_compra##'Y');
END LOOP;

-- ec$ar ar6uivo
utl_file.fclose(vlFile);
END ExpProdutoCSV;
38O9T'4T&+
No esquea de declarar a procedure na especificao da package;
Certifique-se que tem acesso e as permisses necessrias ao diretrio informado
(INTERFACE, em nosso exemplo).
Elaborado por Josinei Barbosa da Silva
Pgina 121 de 154
Treinamento
Embora o nosso exemplo no implemente o tratamento de excees, uma procedure para
gerao de arquivos bem codificada, deve conter esse tratamento. Para isso, veja a tabela 8,
onde as possveis excees de cada sub-rotina de UTL_FILE, esto listadas.
Agora, execute a nova procedure e confira o a arquivo gerado no diretrio que voc especificou.

9ecu%erando in1ormaes de um arquivo
Para testarmos a leitura de arquivos com UTL_FILE, implementaremos mais uma procedure na
package pck_vendas. Essa procedure abrir o arquivo que geramos no exemplo anterior e exibir cada
linha atravs do pacote DBMS_OUTPUT. Abaixo segue o cdigo:
/* 1rocedure 6ue importa o cadastro de produto de um ar6uivo "S4 */
PROCEDURE ImpProdutoCSV IS
vlFile utl_file.file_type;
vlLinha 7ARC)AR4(5649);
BEGIN
-- #7rir ar6uivo
vlFile 23 utl_file.fopen('INTERFACE'='C$%P&o%G'##USER##''cs.'='R');

-- !ecuperar cada lin$a do ar6uivo e exi7ir lin$a atrav@s do d7ms_output
LOOP
BEGIN
utl_file.get_line(vlFile=vlLinha);
dbms_output.put_line(vlLinha);
EXCEPTION
()EN no_data_found T)EN
EXIT;
END;
END LOOP;
-- ec$ar ar6uivo
utl_file.fclose(vlFile);
END ImpProdutoCSV;
38O9T'4T&+
Certifique-se que o arquivo a ser lido est no diretrio utilizado na procedure (INTERFACE,
em nosso exemplo);
No esquea de declarar a procedure na especificao da package;
Para executar a procedure, ligue a exibio de mensagens em sua sesso de SQL: SET
SERVEROUTPUT ON;
Agora s executar e ver o resultado na tela.
T&KT<3O
Embora no faa parte do escopo deste treinamento, vale citar a existncia do pacote TEXT_IO.
Esse pacote semelhante ao UTL_FILE, mas ele permite que voc leia e grave arquivos no cliente
em vez do servidor. TEXT_IO no faz parte do software do banco de dados da Oracle. Ele vem com o
Oracle Developer (Developer 2000).
Elaborado por Josinei Barbosa da Silva
Pgina 122 de 154
Treinamento
Elaborado por Josinei Barbosa da Silva
Pgina 123 de 154
Treinamento
&xerc=cios ro%ostos
1. Crie um pacote chamado pck_vendas que contenha:
Uma funo PIPELINED que receba um intervalo de datas e baseado nessas datas,
monte uma tabela virtual que repita todos os perodos mensais para cada representante
cadastrado. Nomeie essa funo como fnMensalPorRepresentante; Utilize essa funo em uma
declarao SELECT que retorne a venda total mensal por representante em um intervalo de
datas;
Uma funo PIPELINED que receba um intervalo de datas e baseado nessas datas,
monte uma tabela virtual que repita todos os perodos mensais para cada cliente cadastrado.
Nomeie essa funo como fnMensalPorCliente; Utilize essa funo em uma declarao SELECT
que retorne a venda total mensal por cliente em um intervalo de datas;
Uma funo PIPELINED que receba um intervalo de datas e baseado nessas datas,
monte uma tabela virtual que repita todos os perodos mensais para cada produto cadastrado.
Nomeie essa funo como fnMensalPorProduto; Utilize essa funo em uma declarao
SELECT que retorne a venda total mensal por produto em um intervalo de datas;
Uma funo PIPELINED que receba um intervalo de datas e baseado nessas datas,
monte uma tabela virtual que repita todos os perodos mensais para cada Cliente/Produto
cadastrados. A idia obter quanto cada cliente comprou de cada produto em cada perodo.
Nomeie essa funo como fnMensalClienteProduto; Utilize essa funo em uma declarao
SELECT que retorne a venda total mensal por produto em um intervalo de datas;
Elaborado por Josinei Barbosa da Silva
Pgina 124 de 154
Treinamento
0$ T!icos a"anados# SQL e %un&es
incor!oradas do 'racle (ata)ase
1. S#!s avanados %ara usar com !"S#!+
O Outer Join do Oracle
RO(NUM
Comando CASE no S&!&CT
SELECT combinado com CREATE TABLE e INSERT
MERGE
GROUP B< com )A7ING
9ecursos avanados de a*ru%amento+ ROLLUP e CUBE
Consultas /ierrquicas com CONNECT B<
2. (unes Mteis do Oracle Database
Elaborado por Josinei Barbosa da Silva
Pgina 125 de 154
Treinamento
S#!s avanados %ara usar com !"S#!
O outer join do Oracle
Um OUTER JOIN um 5oin simples (chamado de INNER JOIN) com uma "opo estendida " de
trazer tambm os dados que no satisfazem a condio do 5oin, ou seja, em um 5oin entre duas tabelas,
voc tem a opo de trazer os dados, mesmo que no exista um registro correspondente em uma das
tabelas.
Para exemplificar, vejamos a seguinte situao: Elaborar uma SQL que traga os produtos da marca
"Brahma com o resumo mensal de quantidade vendida (ano e ms), existindo ou no o resumo de vendas
do produto. A SQL abaixo, que est no padro ANS92 (tambm chamada de SQL92), nos d essa soluo:
SELECT p.pro_in_codigo
=p.pro_st_nome
=rmv.rmv_in_ano
=rmv.rmv_in_mes
=su!(rmv.rmv_qt_vendida) qt_vendida
FROM produto p LEFT OUTER TOIN resumo_mensal_venda rmv
ON (p.pro_in_codigo 3 rmv.pro_in_codigo)
()ERE p.pro_st_marca 3 'B&$K!$'
GROUP B< p.pro_in_codigo
=p.pro_st_nome
=p.pro_st_marca
=rmv.rmv_in_ano
=rmv.rmv_in_mes
ORDER B< p.pro_in_codigo
=rmv.rmv_in_ano DESC
=rmv.rmv_in_mes DESC;
O LEFT indica que a tabela da esquerda (produto) a base para o OUTER JOIN, ou seja, todos os
registros da tabela PRODUTO que atenderem a clusula WHERE devem ser retornados, mesmo que no
exista resumo de vendas correspondente.
At a, tudo bem! Esse padro de outer 5oin definido pela ANS92 e, portanto, vale para qualquer
banco de dados.
O problema que esse padro veio depois que os SGBDs do mercado j haviam implementado suas
prprias solues para os outer 5oins. No SQL Server, por exemplo, para montar o outer 5oin, bastava incluir
o caracter "* junto do operador "= do lado da tabela base da consulta, ou seja, o nosso exemplo teria as
clusulas FROM e WHERE um pouco diferente:
FROM produto p= resumo_mensal_venda rmv
()ERE p.pro_in_codigo *3 rmv.pro_in_codigo
AND p.pro_st_marca 3 'B&$K!$'
A leitura na implementao do SQL Server bem prxima do padro ANS, pois o "* est do lado da
tabela base que no nosso exemplo LEFT.
No entanto o padro ANS92 pode gerar algumas confuses para quem se habituou com a
implementao de outer 5oin da Oracle.
Diferente do seu concorrente, a implementao da Oracle indica a tabela que pode no haver dados
correspondentes, ao invs da tabela base.
Essa indicao feita pelo operador "(+) e adicionado na associao das tabelas, mas direita da
coluna da tabela que pode no ter registros, como no exemplo abaixo:
Elaborado por Josinei Barbosa da Silva
Pgina 126 de 154
Treinamento
SELECT p.pro_in_codigo
=p.pro_st_nome
=rmv.rmv_in_ano
=rmv.rmv_in_mes
=su!(rmv.rmv_qt_vendida) qt_vendida
FROM produto p= resumo_mensal_venda rmv
()ERE p.pro_in_codigo 3 rmv.pro_in_codigo(P)
AND p.pro_st_marca 3 'B&$K!$'
GROUP B< p.pro_in_codigo
=p.pro_st_nome
=p.pro_st_marca
=rmv.rmv_in_ano
=rmv.rmv_in_mes
ORDER B< p.pro_in_codigo
=rmv.rmv_in_ano DESC
=rmv.rmv_in_mes DESC;
Entre os profissionais Oracle, a implementao da Oracle a mais usada porque:
1. O padro veio depois e implementou uma lgica "contrria a j praticada;
2. A sinalizao do outer 5oin usando apenas "(+) ao invs de um texto em lngua inglesa, gera
SQLs mais limpos;
3. A popularidade do banco de dados Oracle leva esses profissionais a ignorar esse padro.
Dicas+
Se pretende trabalhar com diversos bancos de dados, utilize o padro ANS92;
Se trabalhar exclusivamente com banco de dados Oracle, siga a implementao Oracle,
pois do contrrio ter problemas com outros profissionais dedicados a Oracle;
Se est desenvolvendo ou evoluindo sistemas de um cliente que j possui implementaes
em produo, verifique qual padro utilizam e d sequencia.
9O-458
Existem situaes onde voc executa um SELECT que retorna n registros, mas precisa apenas de
alguns desses registros, no importando qual ou ainda incluir no retorno um nmero sequencial para cada
linha retornada.
O Oracle Database disponibiliza a "pseudo coluna 9O-458 que pode ser utilizada nesses casos.
Veja os exemplos abaixo:
!'<(9M se@uenciando as linhas retornadas
SELECT RO(NUM
=pp.*
FROM preco_produto pp;
!'<(9M limitando a @uantidade de linhas retornadas( $ e0emplo abai0o retorna as 9H maiores
vendas mensais
SELECT RO(NUM
=v.*
Elaborado por Josinei Barbosa da Silva
Pgina 127 de 154
Treinamento
FROM( SELECT p.pro_in_codigo
=p.pro_st_nome
=p.pro_st_marca
=c.cli_st_nome
=rmv.rmv_in_ano
=rmv.rmv_in_mes
=rmv.rmv_qt_vendida
=rmv.rmv_vl_total
FROM produto p
=resumo_mensal_venda rmv
=cliente c
()ERE p.pro_in_codigo 3 rmv.pro_in_codigo
AND rmv.cli_in_codigo 3 c.cli_in_codigo
ORDER B< rmv.rmv_qt_vendida DESC
=rmv.rmv_in_ano DESC
=rmv.rmv_in_mes DESC
=c.cli_st_nome
) v
()ERE RO(NUM @3 56;
Comando C'S& no S&!&CT
Por muito tempo, o comando CASE foi exclusividade da linguagem PL/SQL e para reproduz-lo em
SQLs utilizmos a funo decodeG7, mas nas ltimas verses do Oracle Database, passamos a contar com
esse comando tambm nas SQLs.
O seu uso em SQLs simples e recomendado, pois melhor para o desempenho do SQL do que a
funo decode (embora seja um ganho mnimo).
Abaixo segue um exemplo de uso do CASE em SQLs:
SELECT nfv.nfv_in_numero
=nfv.nfv_dt_emissao
=r.rep_st_nome
=c.cli_st_nome
=c.cli_st_cidade
=(CASE
()EN nfv.nfv_st_condicaopagamento 3 'A7ISTA' T)EN
'A 7ist$'
()EN nfv.nfv_st_condicaopagamento LIHE ';COMTUROS' T)EN
'P$&c/$%o co! Cu&os'
()EN nfv.nfv_st_condicaopagamento LIHE ';SEMTUROS' T)EN
'P$&c/$%o s! Cu&os'
END
) nfv_st_condicaopagamento
FROM nota_fiscal_venda nfv
=cliente c
=representante r
()ERE nfv.cli_in_codigo 3 c.cli_in_codigo
AND nfv.rep_in_codigo 3 r.rep_in_codigo;
S&!&CT combinado com C9&'T& T'B!& e 34S&9T
Elaborado por Josinei Barbosa da Silva
Pgina 128 de 154
Treinamento
C9&'T& T'B!& 'S S&!&CT
possvel criar uma tabela a partir de outra, combinando os comandos CREATE TABLE e SELECT.
Essa combinao um comando DDL (Definition Data Language) e como tal s pode ser executado
em um bloco PL/SQL atravs de SQL Dinmico.
No uma prtica recomendada, mas pode ser til em alguma situao onde voc precise, por
exemplo, criar um backup de uma tabela antes de executar o processamento.
Outro uso bem comum quando voc precisa criar uma cpia de uma tabela sem dados, como no
exemplo abaixo:
CREATE TABLE simula_novo_preco_produto AS
SELECT *
FROM preco_produto
()ERE 534;

38O9T'4T&+ Para executar esse comando, precisar dos privilgios de criao de tabela.
34S&9T N S&!&CT
Se voc precisa executar uma sequencia de INSERTs, originados de uma mesma fonte, sem a
necessidade de tratar cada linha inclusa, voc pode utilizar a combinao dos comandos INSERT e
SELECT.
Com essa combinao, em um nico comando, voc conseguir executar um INSERT para todos os
registros retornados pelo SELECT, como no exemplo abaixo:
INSERT INTO simula_novo_preco_produto
(pro_in_codigo=ppr_st_tipopreco=ppr_vl_unitario)
SELECT pp.pro_in_codigo
=pp.ppr_st_tipopreco
=decode(pp.ppr_st_tipopreco
='COMPRAS'=pp.ppr_vl_unitario
='7ENDAS' =pp.ppr_vl_unitario P(pp.ppr_vl_unitario *6'56))
FROM preco_produto pp;
COMMIT;

38O9T'4T&+ Uma execuo envolvendo um volume muito alto de dados pode resultar em um
problema de desempenho e a soluo pode no ser a ideal. Avalie com responsabilidade o uso desse
recurso.
8&9L&
Esta declarao SQL permite que voc obtenha dados a partir de uma fonte (tabela, view ou consulta
SQL) para manipulao em outra tabela, combinar vrias operaes DML em um nico comando, (como por
exemplo, um UPDATE e um NSERT).
magine uma situao onde voc executa uma consulta em uma tabela e, a partir dos dados obtidos
precise atualizar um registro em outra tabela, mas se o registro no existir, voc o incluir... Vamos criar
essa situao. nclua novos registros na tabela PRODUTO, conforme SQL abaixo:
INSERT INTO produto
(pro_st_nome=pro_in_codigo=pro_st_marca=pro_dt_inclusao
Elaborado por Josinei Barbosa da Silva
Pgina 129 de 154
Treinamento
=pro_st_usuarioinclusao=pro_dt_ultimavenda=pro_vl_ultimavenda
=pro_dt_maiorvenda)
7ALUES
('C&.C$ M$/EBi&'=55='B&$K!$'=S<SDATE
=USER=NULL=NULL=NULL)
1
INSERT INTO produto
(pro_st_nome=pro_in_codigo=pro_st_marca=pro_dt_inclusao
=pro_st_usuarioinclusao=pro_dt_ultimavenda=pro_vl_ultimavenda
=pro_dt_maiorvenda)
7ALUES
('C&.C$ M$/EBi&'=54='ScKinc$&io/'=S<SDATE
=USER=NULL=NULL=NULL)
1
COMMIT
1
Agora, precisamos atualizar o preo de venda dos produtos da marca "Brahma para R$ 1,22, mas se
no existir preo cadastrado para o produto, incluiremos.
O cdigo abaixo resolveria isso:
DECLARE
eRegistroInexistente EXCEPTION;
CURSOR cs_produto IS
SELECT p.pro_in_codigo
FROM produto p
()ERE p.pro_st_marca 3 'B&$K!$';

vNovoValorVenda CONSTANT preco_produto.ppr_vl_unitario;T<PE :3 5'44;

BEGIN
FOR csp IN cs_produto LOOP
BEGIN
UPDATE preco_produto pp
SET pp.ppr_vl_unitario 3 vNovoValorVenda
()ERE pp.pro_in_codigo 3 csp.pro_in_codigo
AND pp.ppr_st_tipopreco 3 '7ENDAS';

IF SIL;NOTFOUND T)EN
RAISE eRegistroInexistente;
END IF;

EXCEPTION
()EN eRegistroInexistente T)EN
INSERT INTO preco_produto(pro_in_codigo=ppr_st_tipopreco=ppr_vl_unitario)
7ALUES(csp.pro_in_codigo='7ENDAS'=vNovoValorVenda);
()EN OT)ERS T)EN
&$isF$,,/ic$tionF&&o&
(G46566= 'N*o +oi ,ossi./ $tu$/iE$& o ,&Vo %o ,&o%uto '
##to_char(csp.pro_in_codigo));
END;

END LOOP;

COMMIT;
END;
Elaborado por Josinei Barbosa da Silva
Pgina 130 de 154
Treinamento
1
Mas possvel executar a mesma alterao utilizando apenas o comando MERGE.
No exemplo abaixo repetimos a operao para alterar o preo de venda dos produtos da marca
"Schincariol (para R$ 1.12) utilizando esse comando:
DECLARE
vNovoValorVenda CONSTANT preco_produto.ppr_vl_unitario;T<PE :3 5'54;
BEGIN
MERGE INTO preco_produto pp
USING (SELECT p.pro_in_codigo
FROM produto p
()ERE p.pro_st_marca 3 'ScKinc$&io/') p
ON (pp.pro_in_codigo 3 p.pro_in_codigo)
()EN MATC)ED T)EN
UPDATE SET pp.ppr_vl_unitario 3 vNovoValorVenda
()ERE pp.ppr_st_tipopreco 3 '7ENDAS'
()EN NOT MATC)ED T)EN
INSERT(pro_in_codigo=ppr_st_tipopreco=ppr_vl_unitario)
7ALUES(p.pro_in_codigo='7ENDAS'=vNovoValorVenda);

COMMIT;
END;
1
GROUP B< com )A7ING
Voc j deve saber que o GROUP BY utilizado para aplicar funes de grupo e dessa forma obter
totais, mdias, valores mximos e mnimos.
Existem situaes onde voc precisa aplicar uma restrio de retorno no valor agrupado ( soma ou
mdia, por exemplo).
Se voc tentar aplicar essa restrio na clusula WHERE receber um erro, pois nela as restries so
em nvel de linha e no de valores agrupados.
Se voc quiser recuperar do banco de dados o total de vendas do ano por marca de produto, no
poder usar a SQL abaixo (tente):
SELECT p.pro_st_marca
=rmv.rmv_in_ano
=su!(rmv.rmv_qt_vendida) rmv_qt_vendida
=su!(rmv.rmv_vl_total) rmv_vl_total
FROM produto p
=resumo_mensal_venda rmv
()ERE p.pro_in_codigo 3 rmv.pro_in_codigo
AND su!(rmv.rmv_vl_total) A :6666
GROUP B< p.pro_st_marca
=rmv.rmv_in_ano
Se tentou, deve ter recebido o erro "$"*-HHIJK( a fun14o de grupo n4o ? permitida a@ui do Oracle.
Para aplicar uma restrio em valor agrupado, voc deve utilizar a clusula HAVING aps o conjunto
Elaborado por Josinei Barbosa da Silva
Pgina 131 de 154
Treinamento
do GROUP BY. Ele funciona como uma clusula WHERE, mas em nvel de grupo. Teste o exemplo abaixo:
SELECT p.pro_st_marca
=rmv.rmv_in_ano
=su!(rmv.rmv_qt_vendida) rmv_qt_vendida
=su!(rmv.rmv_vl_total) rmv_vl_total
FROM produto p
=resumo_mensal_venda rmv
()ERE p.pro_in_codigo 3 rmv.pro_in_codigo
GROUP B< p.pro_st_marca
=rmv.rmv_in_ano
)A7ING su!(rmv.rmv_vl_total) A :6666;
Em nosso exemplo restringimos a soma do valor total (SUM()), mas poderia ser qualquer outra
funo de grupo, como AVG() ou MAX(), por exemplo.
9ecursos avanados de a*ru%amento+ 9O!!5 e C5B&
ROLLUP e CUBE so extenses do GROUP BY, normalmente utilizadas em sistemas de apoio a
deciso, como 'ata3arehouse.
A seguir veremos como utilizar cada um deles.
9O!!5
Com o ROLLUP voc pode criar sub-totais com base nos valores de grupo.
Vamos alterar o nosso exemplo anterior (do GROUP BY com HAVING) para obtermos sub-totais por
ms e ano:
SELECT rmv.rmv_in_ano
=rmv.rmv_in_mes
=p.pro_st_marca
=su!(rmv.rmv_qt_vendida) rmv_qt_vendida
=su!(rmv.rmv_vl_total) rmv_vl_total
FROM produto p
=resumo_mensal_venda rmv
()ERE p.pro_in_codigo 3 rmv.pro_in_codigo
GROUP B< ROLLUP(rmv.rmv_in_ano
=rmv.rmv_in_mes
=p.pro_st_marca);
Observe que alm dos sub-totais, um total geral foi acrescentado no final. Caso no queira o total
geral, deixe a coluna que representa o primeiro nvel (no caso rmv_in_ano) fora do ROLLUP (logo aps a
palavra-chave GROUP BY).
C5B&
Outro recurso avanado para manipular resultados agrupados e que tambm comum para sistemas
de apoio a deciso o CUBE.
O CUBE funciona como o ROLLUP, mas o resultado mais detalhado. Ele apresenta totais para todas
as combinaes de totais do seu GROUP BY. Altere sua consulta para aplicar o CUBE ao invs do ROLLUP e
observe o resultado:
Elaborado por Josinei Barbosa da Silva
Pgina 132 de 154
Treinamento
SELECT rmv.rmv_in_ano
=rmv.rmv_in_mes
=p.pro_st_marca
=su!(rmv.rmv_qt_vendida) rmv_qt_vendida
=su!(rmv.rmv_vl_total) rmv_vl_total
FROM produto p
=resumo_mensal_venda rmv
()ERE p.pro_in_codigo 3 rmv.pro_in_codigo
GROUP B< CUBE(rmv.rmv_in_ano
=rmv.rmv_in_mes
=p.pro_st_marca);
S5L&STJO D& &S#53S'
Existem muitos outros recursos que facilitam a manipulao de dados agrupados garantindo
um bom desempenho, entre eles as 1unes anal=ticas. Pesquise sobre esse recurso e faa
seus testes.
Consultas /ierrquicas com CONNECT B<
O CONNECT BY um recurso do Oracle para recuperarmos uma consulta em ordem hierrquica,
como por exemplo, em um cadastro de funcionrios onde um funcionrio pode ser gerente de outro.
Para estudarmos esse recurso, vamos alterar a nossa tabela de representante para informar o
gerente de cada representante.
Abaixo segue o script para preparar a tabela:
-- incluir coluna
ALTER TABLE representante
ADD(rep_in_codigo_gerente INTEGER)
1
-- registrar o gerente de cada representante
UPDATE representante r
SET r.rep_in_codigo_gerente 3 96
()ERE r.rep_in_codigo 3 56
1
UPDATE representante r
SET r.rep_in_codigo_gerente 3 :6
()ERE r.rep_in_codigo IN(46=86)
1
COMMIT
1
Com a execuo desse script, temos dois gerentes: 40 e 50, sendo que o 40 gerente do
representante 10 e o 50 gerente dos representantes 20 e 30.
Agora, queremos listar os representantes em ordem hierrquica, ou seja, o gerente e em seguida
seus subordinados. A SQL abaixo utiliza o CONNECT BY para isso:
SELECT r.rep_in_codigo "Codigo"
=r.rep_st_nome "Nome"
=r.rep_in_codigo_gerente "Cod.Gerente"
Elaborado por Josinei Barbosa da Silva
Pgina 133 de 154
Treinamento
=r.rep_vl_metamensal "Meta Mensal Vendas"
=r.rep_vl_mediamensalvendas "Media Mensal Vendas"
=r.rep_dt_maiorvenda "Valor Maior Venda"
=r.rep_dt_maiorvenda "Data Maior Venda"
FROM representante r
START (IT) r.rep_in_codigo_gerente IS NULL
CONNECT B< PRIOR rep_in_codigo 3 rep_in_codigo_gerente;
Entendendo a SQL:
Na clusula START WITH voc defini o incio de sua hierarquia, que em nosso caso so os
representantes que esto no "topo da pirmide, ou seja, no possuem gerente.
Na clusula CONNET BY PRIOR voc indica a associao do nvel mais baixo com o mais
alto. Se traduzssemos ao p da letra, seria algo como "Conecte este representante abaixo do
representante indicado como gerente.
Com o CONNECT BY voc tambm pode visualizar em que nvel da hierarquia cada registro se
encontra. Para isso, existe uma "pseudo coluna chamada LEVEL.
Voc pode utilizar LEVEL na lista de colunas:
SELECT LE7EL
=r.rep_in_codigo "Codigo"
=r.rep_st_nome "Nome"
=r.rep_in_codigo_gerente "Cod.Gerente"
=r.rep_vl_metamensal "Meta Mensal Vendas"
=r.rep_vl_mediamensalvendas "Media Mensal Vendas"
=r.rep_dt_maiorvenda "Valor Maior Venda"
=r.rep_dt_maiorvenda "Data Maior Venda"
FROM representante r
START (IT) r.rep_in_codigo_gerente IS NULL
CONNECT B< PRIOR rep_in_codigo 3 rep_in_codigo_gerente;
LEVEL tambm pode ser utilizado como restrio da consulta. Veja como fica nossa consulta se
quisermos apenas visualizar os gerentes:
SELECT LE7EL
=r.rep_in_codigo "Codigo"
=r.rep_st_nome "Nome"
=r.rep_in_codigo_gerente "Cod.Gerente"
=r.rep_vl_metamensal "Meta Mensal Vendas"
=r.rep_vl_mediamensalvendas "Media Mensal Vendas"
=r.rep_dt_maiorvenda "Valor Maior Venda"
=r.rep_dt_maiorvenda "Data Maior Venda"
FROM representante r
()ERE LE7EL 3 5
START (IT) r.rep_in_codigo_gerente IS NULL
CONNECT B< PRIOR rep_in_codigo 3 rep_in_codigo_gerente;
(unes incor%oradas do Oracle Database
Existem muitas funes incorporadas do Oracle Database que podem otimizar o seu trabalho, tanto
em tempo de desenvolvimento quanto desempenho do que voc produzir.
Em geral essas funes podem ser usadas em cdigos PL/SQL e declaraes SQL.
impossvel memorizar todas essas funes, mas a seguir, listamos algumas das mais comuns e
Elaborado por Josinei Barbosa da Silva
Pgina 134 de 154
Treinamento
mais teis:
Tabela 9: Fun29es de caractere incor#oradas do 'racle
%uno Descrio
CHR Retorna um caractere quando recebe seu valor ASCII.
ASCII Retorna o cdigo ASCII do caractere.
INITCAP Retorna uma string na qual a primeira letra de cada palavra
colocada em maiscula e todos os caracteres restantes, em
minsculas.
INSTR Retorna a localizao de uma string dentro de outra string.
LENGTH Retorna o comprimento de uma string de caracteres. Retorna
NULL quando o valor NULL.
LOWER Converte toda a string de caracteres para minsculas.
LPAD Preenche uma string no lado esquerdo com qualquer string
especificada.
LTRIM Corta uma string de caracteres do lado esquerdo.
REPLACE Substitui toda ocorrncia de uma string por outra string8
RPAD Preenche uma string no lado direito de toda string
especificada.
RTRIM Corta uma string de caracteres no lado direito.
SUBSTR Retorna uma parte de uma string de dentro de uma string.
TRIM Combina a funcionalidade das funes LTRIM e RTRIM.
TRANSLATE Igual a REPLACE, exceto que opera no nvel de caractere, em
vez de operar no nvel de string.
UPPER Converte toda a string de caracteres para maisculas.
Tabela 10: Fun29es num?ricas incor#oradas do 'racle
%uno Descrio
ABS Retorna o valor absoluto de um nmero
CEIL Retorna o valor que representa o menor inteiro que maior do
que ou igual a um nmero especificado. Muito usado para
arrendondar valores para baixo.
EXP Retorna a exponenciao de e elevado potncia de algum
nmero onde e= 2,7182818...
LN Retorna o logaritmo natural de algum nmero x.
MOD Retorna o resto de algum nmero x dividido por algum nmero
y.
ROUND Retorna x arredondado para y casas.
SQRT Retorna a raiz quadrada de algum nmero x. X nunca deve ser
negativo.
TRUNC Retorna algum nmero x, truncado para y casas. No arredonda,
apenas corta na localizao especificada.
Tabela 11: Fun29es de data incor#oradas do 'racle
%uno Descrio
ADD_MONTHS Adiciona um ms data especificada. Ela no adiciona 30 ou
31 dias, mas simplesmente adiciona um ao ms. Se o nmero de
meses informado for negativo, subtrai os meses.
LAST_DAY Retorna o ltimo dia de determinado ms.
MONTHS_BETWEEN Calcula os meses entre duas datas. Retorna um inteiro quando
ambas as datas so os ltimos dias do ms. Caso contrrio,
ela retorna a parte fracionria de um ms de 31 dias.
Elaborado por Josinei Barbosa da Silva
Pgina 135 de 154
Treinamento
%uno Descrio
NEXT_DAY Retorna a data do primeiro dia da semana especificado em uma
string aps a data inicial.
SYSDATE Simplesmente retorna a data e hora do sistema no formato tipo
DATE.
TRUNC Trunca at o parmetro de data especificado, tal como dia,
ms e assim por diante. Normalmente utilizado sem parmetros,
trunca na data, retirando hora, minutos e segundos.
Tabela 12: Fun29es de con)ers3o incor#oradas do 'racle
%uno Descrio
TO_CHAR Converte DATEs e NUMBERs em uma string VARCHAR2.
TO_DATE Converte uma string CHAR ou VARCHAR2 em um valor DATE.
TO_NUMBER Converte uma string CHAR ou VARCHAR2 em um valor NUMBER.
Tabela 13: Fun29es di)ersas incor#oradas do 'racle
%uno Descrio
DECODE Age como uma declarao IF...THEN...ELSE de uma lista de
valores.
GRATEST Toma uma lista de valores ou expresses e retorna o maior
valor avaliado.
NVL Verifica se o valor passado como primeiro parmetro nulo e
sendo, retorna o segundo parmetro como substituto. (simula a
atribuio de valor default)
USER Retorna o nome do usurio atual em uma string VARCHAR2.
USERENV Retorna as informaes sobre o seu ambiente de trabalho
atual.
Dicas
Sempre utilize TO_DATE quando estiver referenciando uma data em formato string.
Um cdigo bem feito segue essa prtica informando a data (em formato string) e
um segundo parmetro com informando em que formato encontra a data a ser
convertida (esse parmetro tambm um string);
Ao referenciar variveis que no podem conter valor nulo, utilize a funo NVL();
Para exibir uma data armazenada no banco de dados ou retornada por SYSDATE,
em um formato especfico, utilize a funo TO_CHAR;
Utilize a funo CHR() para formatar suas mensagens de sistema com quebra de
linha (chr(13));
Se pretende utilizar um valor alfanumrico em uma expresso matemtica,
converta seu valor utilizando TO_NUMBER();
Antes de implementar uma funo ou uma lgica para tratar ou converter um
dado, pesquise se j no existe o que precisa entre as funes incorporadas do
Oracle Database.
Elaborado por Josinei Barbosa da Silva
Pgina 136 de 154
Treinamento
0* (icas de !er%or+ance e
)oas !r,ticas e+ SQL
1. Otimi>ador do Oracle;
2. :ariveis de li*ao 6Bind :ariables7;
3. S#! Din0mico;
4. &K!'34 !'34;
5. S#!s Com%lexas;
6. SO9T 6Ordenao nas S#!s7;
7. etc@@@
Elaborado por Josinei Barbosa da Silva
Pgina 137 de 154
Treinamento
3ntroduo
Problemas com aplicaes de baixa performance podem estar frequentemente relacionados a
consultas SQL mal estruturadas ou a uma modelagem de banco de dados ineficiente.
A metodologia de tuning da Oracle, recomenda que, antes de analisar configuraes do banco de
dados, seja analisadas e ajustadas as instrues SQL que apresentarem desempenho insatisfatrio.
A otimizao de uma instruo SQL constitui em determinar a melhor estratgia para execut-la no
banco de dados. O otimizador do Oracle escolhe, por exemplo, se usar um ndice ou no para uma
consulta especifica e que tcnicas de 5oin usar na juno de mltiplas tabelas. Estas decises tm um
impacto muito grande na performance de um SQL e por isso a otimizao de uma instruo essencial
para qualquer aplicao e de extrema importncia para a performance de um banco de dados relacional.
muito importante que os desenvolvedores conheam o otimizador do Oracle como tambm os
conceitos bsicos relativos tuning. Tal conhecimento ir ajudar a escrever consultas muito mais eficientes
e rpidas.
Alm de conhecer o otimizador do Oracle, imprescindvel que o desenvolvedor conhea a aplicao
e os dados dela. Antes de sair escrevendo uma consulta SQL, procure entender o processo do qual essa
instruo far parte. Qual a finalidade dessa instruo? nformaes idnticas podem ser encontradas em
diferentes fontes de dados. Se voc estiver familiarizado com estas fontes, poder identificar a fonte que
proporcionar uma recuperao mais rpido, ou seja, uma consulta em menor tempo.
O Otimi>ador Oracle
O otimizador determina a maneira mais eficiente de se rodar uma declarao SQL. Para executar
qualquer SQL o Oracle tem que montar um plano de e0ecu14o. O plano de execuo de uma consulta
uma descrio de como o Oracle ir implementar a recuperao dos dados para satisfazer a uma
determinada declarao SQL.
At a verso 9i, o Oracle possuia dois otimizadores:
9BO (Ruled Based Optimizer): Otimizador baseado em regra;
CBO (Cost Based Optimizer): Otimizador baseado em custo, que passou a ser o padro na
verso 9i.
A partir da verso 10g do Oracle, o otimizador baseado em regra (RBO) deixou de ser utilizado e o
otimizador baseado em custo (CBO) passou a ser o nico existente.
Mesmo com o Oracle 10g utilizando apenas o CBO, vale a pena conhecermos os dois e o que
veremos a seguir.
Otimi>ador baseado em re*ra 69BO7
O RBO utiliza uma srie de regras rgidas para determinar um plano de execuo para cada SQL. Se
voc conhecer as regras voc pode construir uma consulta SQL para acessar os dados da maneira
desejada.
S pra exemplificar, quando voc criava um ndice na sua tabela e monta uma SQL para acess-la,
filtrando as colunas do ndice criado, sendo o otimizador RBO, com certeza ele utilizaria esse ndice para
acesso. como se obedecesse uma ordem sua. O problema que o simples fato de existir um ndice que
corresponde ao seu filtro, no quer dizer que ele seja seletivo e se no for, causa efeito contrrio. Talvez
seja mais eficiente acessar toda a tabela (o famosso TABLE ACCESS FULL) do que acessar um ndice a
procura de um ponteiro.
O RBO deixou de ser aperfeioado na verso 9i do Oracle Database, quando o CBO, que veremos a
seguir, passou a ser o otimizador recomendado.
Elaborado por Josinei Barbosa da Silva
Pgina 138 de 154
Treinamento
No Oracle 10g foi descontinuado.
Otimi>ador baseado em custo 6CBO7
ntroduzido no Oracle 7, o CBO tentar achar o plano de execuo que possui o menor custo para
acessar os dados tanto para uma quantidade de trabalho especifica como para um tempo inicial de resposta
mais rpido. Os Custos de diferentes planos so calculados e a opo que apresentar o menor custo de
execuo escolhida. So coletadas estatsticas referentes s tabelas do banco de dados e estas so
usadas para determinar um plano de execuo timo.
A fonte de informaes do otimizador CBO so as estatsticas coletadas no banco de dados e essa
uma das razes que levam o Oracle 10g a adotar a coleta de estatsticas automtica, uma vez que s
trabalha com o otimizador CBO.
38O9T'4T&+ Um banco de dados configurado para trabalhar com CBO que no tiver uma poltica
de atualizao peridica de estatsticas, provavelmente ser vtima de desempenho ruim.
Seletividade
A seletividade a primeira e mais importante medida do Otimizador Baseado em Custo. Ela
representa uma frao de linhas de um conjunto resultante de uma tabela ou o resultado de um 5oin ou um
agrupamento.
O CBO utiliza estatsticas para determinar a seletividade de um determinado predicado (clausula
WHERE ou HAVING). A seletividade diretamente ligada ao predicado da consulta, como por exemplo
()ERE PRO_IN_CODIGO 3 549:
Ou uma combinao de predicados, como:
()ERE PRO_IN_CODIGO 3 549:
AND PRO_ST_CESTOQUE3'S'
O propsito do predicado de uma consulta limitar o escopo dela a um certo nmero de linhas em
que estamos interessados. Portanto, a seletividade de um predicado indica quantas linhas de um conjunto
vo ser filtradas por uma determinada condio.
A seletividade varia numa faixa de valores de 0.0 at 1.0 onde a seletividade de 0 indica que
nenhuma linha ser selecionada e 1 que todas as linhas sero selecionadas. A seletividade igual ao
numero de valores distintos que uma coluna possui (1/NVD onde NVD significa o Numero de Valores
Distintos).
:ariveis de li*ao 6Bind :ariables7
As variveis de ligao (bind) permitem que uma instruo SQL seja preparada uma nica vez pelo
banco de dados e executada inmeras vezes, mesmo com valores diferentes para estas variveis. Esta
economia na fase de preparao a cada execuo representa um ganho de eficincia (tempo e recursos) na
aplicao e no servidor de banco de dados.
Alm disso, variveis de ligao facilitam a validao de tipo de dados dos valores de entrada
fornecidos dinamicamente e evitam os riscos de vulnerabilidade de segurana e integridade existentes
quando se constri uma instruo SQL por concatenao de strings. Assim, este recurso trs tambm
robustez e segurana execuo de SQL nas aplicaes.
Por exemplo, imagine que voc precise buscar de dentro de uma aplicao Delphi a descrio de um
determinado produto a partir de um cdigo informado. Uma funo (em Delphi) como abaixo pode resolver
Elaborado por Josinei Barbosa da Silva
Pgina 139 de 154
Treinamento
isso:
+unction DescricaoProduto(pCodProduto: intL&): st&inL;
BLin
ZitK Query1 do
BLin
SIL.C/os;
SIL.C/$&;
SIL.A%%('SELECT PROFSTFNOME');
SIL.A%%('FROM PRODUTO');
SIL.A%%('()ERE PROFINFCODIGO 3 ' P pCodProduto);
SIL.O,n;
Rsu/t :3 FieldByName('PROFSTFNOME').AsString;
n%;
n%;
O problema que toda vez a declarao SELECT dessa funo for executada, o Oracle vai fazer uma
anlise da instruo para definir o plano de execuo (o chamado PARSE). sso acontece porque o cdigo
do produto, recebido como argumento, concatenado SQL, ou seja, para o Oracle essa mesma instruo,
quando o cdigo for 1 diferente de quando o cdigo for 2.
Outro impacto negativo dessa instruo refere-se ao uso de memria. O servidor Oracle possui uma
rea de memria compartilhada que aloca as instrues SQLs executadas mais recentemente. Quando uma
instruo recebida pelo servidor, antes de fazer o seu PARSE, ele verifica se essa instruo existe nessa
rea. Se existir, ele utiliza o plano que j foi definido (e que fica alocado com a instruo). Muito bem e da?
Da que se o Oracle acha que a instruo acima com o cdigo 1 diferente da mesma instruo com o
cdigo 2, ele vai consumir dois espaos de memria. Agora imagine um sistema com mil usurios
conectados e todas as instrues SQLs escritas dessa forma!
A SQL do exemplo acima, ficaria alocado na memria da servidor Oracle da seguinte forma:
Quando for cdigo 1
SELECT PRO_ST_NOME
FROM PRODUTO
()ERE PRO_IN_CODIGO 3 5
Quando for cdigo 2
SELECT PRO_ST_NOME
FROM PRODUTO
()ERE PRO_IN_CODIGO 3 4
Literalmente, as duas instrues acima so diferentes.
Para que o Oracle reaproveite as instrues, eficientemente, MPORTANTSSMO que os
aplicativos faam uso das chamadas BE&' #*"E*BLES ao executar SQLs.
Abaixo, segue a mesma funo Delphi do exemplo anterior, utilizando esse recurso:
+unction DescricaoProduto(pCodProduto: intL&): st&inL;
BLin
ZitK Query1 do
BLin
SIL.C/os;
Elaborado por Josinei Barbosa da Silva
Pgina 140 de 154
Treinamento
SIL.C/$&;
SIL.A%%('SELECT PROFSTFNOME');
SIL.A%%('FROM PRODUTO');
SIL.A%%('()ERE PROFINFCODIGO 3 2PROFINFCODIGO');
ParamByName('PROFINFCODIGO').AsInteger :3 pCodProduto;
SIL.O,n;
Rsu/t :3 FieldByName('PROFSTFNOME').AsString;
n%;
n%;
A BE&' #*"E*BLE indicada pelos dois pontos dentro da declarao SQL (:PRO_IN_CODIGO). O
nome da variable livre, mas observe que no nosso caso foi utilizado o nome da coluna que est sendo
filtrada. &sse a%enas um %adro.
Bem, esta instruo na memria do servidor Oracle ficar semelhante com o demonstrado abaixo:
SELECT PRO_ST_NOME
FROM PRODUTO
()ERE PRO_IN_CODIGO 3 :5
Este ":1 no representa o cdigo de produto 1 e sim uma BE&' #*"E*BLE. Essa varivel assumir
como valor qualquer cdigo de produto informado na aplicao, ou seja, sempre ser a mesma instruo,
no importando a quantidade de vezes que ela for executada e, consequentemente, o mesmo plano de
execuo (j armazenado da primeira vez).
Portanto, h grande importncia e vantagens no uso de SQL que usam variveis de ligao (bind) em
aplicaes que interagem com bancos de dados, especialmente quando envolvem valores dinmicos e
parmetros fornecidos pelo usurio, de forma que este recurso deve ser utilizado sempre, tratando-se de
boa prtica de programao.
S#! Din0mico
Tudo que falamos sobre BE&' VARABLE, pode induzir o leitor a achar que o recurso de SQL
Dinmico da PL/SQL funciona de forma semelhante. &4L'4OO
Os SQLs Dinmicos do PL/SQL SEMPRE so submetidos ao PARSE e SEMPRE ocupam lugar
exclusivo na memria.
Quando desenvolver rotinas PL/SQL, procure escrever SQLs nativos (estticos).
Por exemplo, na funo pck_cliente.GetMediaCompraMensal poderia ser reescrita da seguinte
forma:
-- uno 6ue retorna a media de compra ;geral>
-- por ramo de atividade> cidade ou ramo E cidade=
FUNCTION GetMediaCompraMensal
(pRamo cliente.cli_st_ramoatividade;T<PE
=pCidade cliente.cli_st_cidade;T<PE)
RETURN NUMBER IS
vlResult cliente.cli_vl_mediacomprasmensal;T<PE 23 6;
BEGIN

SELECT A7G(c.cli_vl_mediacomprasmensal)
INTO vlResult
Elaborado por Josinei Barbosa da Silva
Pgina 141 de 154
Treinamento
FROM cliente c
()ERE c.cli_st_ramoatividade 3
decode(pRamo=NULL=c.cli_st_ramoatividade=pRamo)
AND c.cli_st_cidade 3
decode(pCidade=NULL=c.cli_st_cidade=pCidade);

RETURN(vlResult);
EXCEPTION
()EN no_data_found T)EN
RETURN(6);
()EN OT)ERS T)EN
raise_application_error
(G46566= 'N*o +oi ,ossi./ &cu,&$& !O%i$ % co!,&$ ,$&$2 '##
chr(58)##'R$!o2 '##pRamo##
chr(58)##'Ci%$%2 '##pCidade##
chr(58)##
chr(58)##'E&&o2 '##s0/&&!);

END GetMediaCompraMensal;
38O9T'4T&+ no exemplo acima, aplicamos uma funo do Oracle nas condies da consulta. Esse
tipo de implementao deve ser avaliado caso a caso, pois pode afetar a performance (negativamente). Via
de regra, os problemas acontecem quando a funo aplicada na coluna e no no valor de restrio.
O uso de =ndices
Para tirar vantagem dos ndices, escreva seu SQL de uma maneira que o Oracle faa uso dele. O
otimizador do Oracle no usar o acesso atravs de um ndice simplesmente porque ele existe em uma
coluna, caso o otimizador seja por custo.
Lembre-se tambm que um ndice NO SELETVO pode ser ignorado pelo otimizador CBO.
Tenha certeza de ter criado todos os ndices necessrios nas tabelas, mas tome cuidado com o
excesso de ndices, pois eles podem degradar a performance de DMLs na tabela. Ento como escolher que
colunas indexar?
Use ndices em colunas que so frequentemente usados na clausula WHERE de consultas da
aplicao ou de consultas usadas por usurios finais.
ndexe as colunas que so frequentemente usadas para juntar 5oin as tabelas nas diferentes
consultas. Prefira fazer Foin pelas chaves primarias e chaves estrangeiras. Use ndices apenas em
colunas que possuem uma baixa porcentagem de linhas com valores iguais (isso est diretamente
li*ado a seletividade).
No use ndices em colunas que so usadas apenas com funes e operadores NO
EXATOS (<, <=, >, >=, <>, !=) na clausula WHERE, pois a partir do momento em que voc aplica
uma funo coluna ou utiliza um operador que permite um range de valores, o ndice ignorado
pois vai apontar para n possveis entradas.
No indexe colunas que so frequentemente modificadas ou quando a eficincia ganha
atravs da criao de um ndice no valha a pena devido perda de performance em operaes de
INSERT, UPDATE e DELETE. Com a criao do ndice, estas operaes perdero em performance
devido necessidade de manter o ndice correto.
ndices nicos (UNIQUE) so melhores que os no nicos devido a melhor seletividade. Use
ndices nicos em chaves primrias e ndices no nicos em chaves estrangeiras (FOREIGN KEY) e
colunas muito usadas nas clausulas WHERE;
Elaborado por Josinei Barbosa da Silva
Pgina 142 de 154
Treinamento
':'!3& S&89& a possibilidade de criar um ndice para cada FOREIGN KEY da tabela. sso
pode resultar em grandes ganhos de desempenho em instrues onde ocorre o relacionamento
entre as tabelas.
Colunas indexadas no ORDER B<
O otimizador do Oracle ir utilizar uma varredura por ndice se a clausula ORDER BY possuir uma
coluna indexada. Uma consulta usar o ndice criado para uma coluna, mesmo que a coluna no seja
especificada na clausula WHERE. A consulta obter o ROWID para cada linha do ndice e acessar a tabela
usando o ROWID.
&K!'34 !'34
Se familiarize com a ferramenta EXPLAIN PLAN e use-a para otimizar seu SQL. O EXPLAIN PLAN
ir te ajudar a descobrir, atravs do plano de execuo da consulta, os meios de acesso que o Oracle est
utilizando para acessar as tabelas do banco de dados.
Uma vez que identificamos um SQL com uma performance ruim, podemos usar o comando EXPLAIN
PLAN FOR para gerar um plano de execuo para este SQL.
Para usar esse comando necessrio criar a tabela PLAN_TABLE, atravs do script utlxplan.sql,
que normalmente fica em "Oracle_home/rdbms/admin"
Vejamos um exemplo no SQL*Plus de como usar o EXPLAIN PLAN:
EXPLAIN PLAN FOR
SELECT n.*=
i.*
FROM nota_fiscal_venda n=
item_nota_fiscal_venda i
()ERE n.nfv_in_numero 3 i.nfv_in_numero;
Desta forma estamos populando a tabela PLAN_TABLE com o plano de execuo do SQL. A consulta
no executada. Apenas o plano de execuo gerado.
Mas como visualizamos o plano?
Antes de executar o EXPLAIN PLAN, certifique-se de truncar a tabela PLAN_TABLE ou de usar o
comando "SET STATEMENT_ID=" nas execues do comando EXPLAIN PLAN FOR, pois de outra forma o
plano de execuo de diversas consultas ser guardado na PLAN TABLE tornando sua interpretao
extremamente complicada.
Ento, vamos recriar nosso plano de execuo definindo um STATEMENT_ID:
EXPLAIN PLAN SET STATEMENTFID 3 'XXX' FOR
SELECT n.*=
i.*
FROM nota_fiscal_venda n=
item_nota_fiscal_venda i
()ERE n.nfv_in_numero 3 i.nfv_in_numero;
Agora voc pode consultar a PLAN TABLE para ver como a consulta ser executada. A consulta a
seguir pode ser usada para extrair a parte importante do plano de execuo da tabela PLAN TABLE.
SELECT LPAD(' '=4*(LE7ELG5))##
OPERATION##' '##
OPTIONS##' '##' '##
OBJECT_NAME##' '##
Elaborado por Josinei Barbosa da Silva
Pgina 143 de 154
Treinamento
DECODE(ID= 6= 'COST 3 '##POSITION) "QUERY PLAN"
FROM PLAN_TABLE
START (IT) ID 3 6 AND STATEMENTFID 3 'XXX'
CONNECT B< PRIOR ID 3 PARENT_ID AND STATEMENTFID 3 'XXX';
O plano de execuo da consulta ficar como a seguinte amostra:
IUER< PLAN
------------------------------------------------------------------------------
SELECT STATEMENT COST 3 55
MERGE TOIN
SORT TOIN
TABLE ACCESS FULL NOTA_FISCAL_VENDA
SORT TOIN
TABLE ACCESS FULL ITEM_NOTA_FISCAL_VENDA
38O9T'4T&+ Para que o o custo da declarao (COST) seja informado, necessrio que
estatsticas dos objetos acessados pela declarao estejam devidamente coletados. Essas estatsticas
podem ser coletadas atravs do pacote dbms_stats. O procedimento gather_schema_stats, por
exemplo, faz a coleta de estatsticas de todos os objetos do s@uema. NO exemplo abaixo coletada as
estatsticas do schema JSILVA:
SILA xcut dbms_stats.gather_schema_stats(ownname 3A 'TSIL7A');
Existem boas ferramentas de programao que possuem o EXPLAN, como: Toad, Oracle Sql
Developer, PLSQL Developer, etc..
O '5TOT9'C& do S#!)lus
O AUTOTRACE um recurso do SQL*Plus que permite gerar, automaticamente, um relatrio baseado
no plano de execuo usado pelo otimizador assim como tambm as estatsticas referentes execuo
daquele SQL. O Relatrio gerado aps a execuo com sucesso de comandos DML (SELECT, DELETE,
UPDATE e INSERT). Ele usado para monitorar e otimizar a performance dessas declaraes.
O AUTOTRACE pode ser utilizado de cinco maneiras:
1. SET AUTOTRACE OFF: Nenhum relatrio de AUTOTRACE gerado. Esta a opo padro
('efault).
2. SET AUTOTRACE ON EXPLAIN: O relatrio de AUTOTRACE mostra apenas o Plano de
execuo.
3. SET AUTOTRACE ON STATISTICS: O relatrio de AUTOTRACE mostra apenas as
estatsticas referentes a execuo do SQL.
4. SET AUTOTRACE ON: O Relatrio de AUTOTRACE inclui tanto o Plano de execuo como as
estatsticas referentes execuo do SQL. Tambm requer executar o script plustrce.sql e
receber alguns privilgios de DBA.
5. SET AUTOTRACE TRACEONLY: Funciona como o SET AUTOTRACE ON, mas suprime a
impresso do resultado da declarao se ela possuir. Tambm requer executar o script
plustrce.sql e receber alguns privilgios de DBA.
38O9T'4T&+ Para utilizar as opes que exibem estatsticas necessrio:
Elaborado por Josinei Barbosa da Silva
Pgina 144 de 154
Treinamento
Executar o script plustrce.sql, que fica em ORACLE_HOME\sqlplus\admin com o
usurio SYS (como SYSDBA);
Ainda com o usurio SYS, atribuir o role PLUSTRACE para o usurio que far uso do recurso,
caso no seja o prprio DBA.
Exemplo:
1. Ative o SET AUTOTRACE TRACEONLY para exibir o plano de execuo e as estatsticas,
sem as linhas de retorno:
SILA SET AUTOTRACE TRACEONL<;
2. Agora execute a seguinte consulta:
SELECT n.nfv_in_numero=
i.pro_in_codigo
FROM nota_fiscal_venda n=
item_nota_fiscal_venda i
()ERE n.nfv_in_numero 3 i.nfv_in_numero;
3. O resultado deve ser parecido com:
Execution P/$n
----------------------------------------------------------
6 SELECT STATEMENT Optimizer3C)OOSE (Cost39 Card3N6 Bytes3[R6)
5 6 NESTED LOOPS (Cost39 Card3N6 Bytes3[R6)
4 5 TABLE ACCESS (FULL) OF 'ITEMFNOTAFFISCALF7ENDA' (Cost39
Card3N6 Bytes39R6)
8 5 INDEX (UNIIUE SCAN) OF 'PHFNOTAFFISCALF7ENDA' (UNIIUE)
St$tistics
----------------------------------------------------------
6 recursive calls
6 db B/ocS gets
49 consistnt gets
6 physical &$%s
6 redo siE
59N[ bytes sent via SIL*Net to client
:84 bytes received via SIL*Net +&o! client
: SIL*Net roundtrips to1+&o! client
6 sorts (memory)
6 sorts (%isS)
N6 &oZs processed
Lendo a saFda de um #9C'C!#")
No exemplo acima, onde usamos o AUTOTRACE TRACEONLY, temos o plano de execuo e as
estatsticas, mas como interpretamos isso?
O plano de execuo:
Opitmizer indica o otimizador Oracle usado: CHOOSE ou RULE (Custo ou Regra);
Cost indica o custo em cada ponto do plano, ou seja, o custo da SELECT STATEMENT o
Elaborado por Josinei Barbosa da Silva
Pgina 145 de 154
Treinamento
custo total da declarao;
Card indica a cardinalidade em cada ponto do plano. Entenda como cardinalidade o nmero
de relacionamentos para recuperar os dados;
Bytes indica o nmero de bytes envolvidos em cada ponto do plano;
Nested, como o nome j diz, algo aninhado e no nosso exemplo temos NESTED LOOP, ou
seja, um LOOP aninhado que foi gerado devido ao relacionamento de masterDdetail entre nota fiscal
e item nota fiscal;
TABLE ACCESS (FULL) indica o objeto em que foi realizado um acesso completo, ou seja,
no nosso caso todas as linhas da tabela de item nota fiscal foram verificadas. sso no bom em
tabelas com grande e mdio volumes de dados. O ideal um acesso parcial atravs de um indce,
como aconteceu com a tabela de notas;
INDEX SCAN indica o ndice utilizado para acessar um conjunto de dados, no nosso caso, na
tabela de nota fiscal foi usada a chave primria.
As estatsticas:
recursive calls: Nmero de chamadas recursivas dentro da instruo;
db block gets: o nmero de vezes que um bloco foi requisitado para o buffer cache;
consistent gets; o nmero de vezes que uma leitura consistente foi requisitada para
um bloco do buffer cache.
physical reads: o nmero total de blocos de dados lidos do disco para o buffer cache.
redo size: o tamanho (em bytes) do log redo gerado durante essa operao;
bytes sent via SQL*Net to client: Total de bytes enviados ao cliente pelos
processos de segundo plano;
bytes received via SQL*Net from client: Total de bytes enviados ao servidor pelo
cliente;
SQL*Net roundtrips to/from client: Nmero total de mensagens enviadas e
recebidas pelo cliente;
sorts (memory): Nmero de ordenaes em memria;
sorts (disk): Nmero de ordenaes em disco. sso muito ruim para o desempenho
pois executa /O;
rows processed: Nmero de linhas processadas na operao.
E&'EL S*& versus 6%LL !*BLE S*&
Se voc estiver selecionando mais de 15 % das linhas de uma tabela, um FULL TABLE SCAN
geralmente mais rpido do que o acesso pelo ndice. Quando o acesso por ndice causar lentido ao invs
de apresentar um ganho de performance, voc pode utilizar algumas tcnicas para eliminar o uso do ndice:
SELECT *
FROM produto p
()ERE p.pro_vl_ultimavendaP6 3 56666;
Supondo que existisse um ndice na coluna pro_vl_ultimavenda, ele no seria usado, pois o
Elaborado por Josinei Barbosa da Silva
Pgina 146 de 154
Treinamento
ndice seria pela coluna pro_vl_ultimavenda e no pela coluna mais zero.
Um ndice tambm no usado se o Oracle tiver que realizar uma converso implcita de dados. No
nosso exemplo, pro_vl_ultimavenda do tipo NUMBER, ou seja, se filtrarmos o valor como string, o
ndice no ser usado:
SELECT *
FROM produto p
()ERE p.pro_vl_ultimavenda 3 '56666';
Essa manobra tambm poderia ser realizada aplicando uma funo na coluna filtrada, sem que isso
afetasse seu contedo, claro.
C53D'DO+ Lembre-se sempre que essa teoria s vale quando estamos selecionando mais do que 15%
dos dados e, mesmo assim, deve ser analisada com ateno, pois as excees tambm existem. Tenha
responsabilidade no uso dessas tcnicas.
A clusula WHERE crucial!
nforme o mximo de restries possveis na clausula WHERE, pois isso diminuir o universo dos
dados a serem recuperados, mas tome cuidado com algumas situaes onde o uso de restries pode ser
prejudicial, principalmente para a utilizao de ndices.
As seguintes clusulas no WHERE no faro uso do ndice mesmo que ele esteja disponvel:
A.COL1 > A.COL2
A.COL1 < A.COL2
A.COL1 >= A.COL2
A.COL1 <= A.COL2
COL1 S NULL
COL1 S NOT NULL.
Uma entrada de ndice possui ponteiros para valores exatos da tabela e no um range de valores,
logo o uso de operadores >, <, >= e <=, inviabilizam o uso de um ndice.
Um ndice no guarda o ROWID (que o ponteiro) de colunas que possuem valores nulos. Qualquer
consulta em linhas que possuam valores nulos o ndice no pode ser utilizado.
COL1 NOT N (value1, value2 )
COL1 != expression
COL1 LKE '%teste'
Neste caso, o uso do "%" no inicio da string acaba por suprimir a parte por onde coluna indexada e
por isso o ndice no usado. Por outro lado, COL1 LKE 'teste%' ou COL1 LKE 'teste%teste%' faz uso do
ndice resultando em uma busca por faixas limites.
UPPER(COL1) = 'TESTE'
Quaisquer expresses, funes ou clculos envolvendo colunas indexadas no faro uso do ndice se
Elaborado por Josinei Barbosa da Silva
Pgina 147 de 154
Treinamento
ele existir. No exemplo acima, o uso da funo UPPER vai impedir do ndice ser usado.
5se o ()ERE ao invs do )A7ING %ara 1iltrar lin/as
Evite o uso da clausula HAVING junto com GROUP BY em uma coluna indexada. Neste caso o ndice
no utilizado. Alm disso, exclua as linhas indesejadas na sua consulta utilizando a clausula WHERE ao
invs do HAVING. Se a tabela produto possusse um ndice na coluna pro_st_marca, a seguinte
consulta no faria uso dele:
SELECT p.pro_st_marca=
A7G(p.pro_vl_maiorvenda) pro_vl_mediamaiorvenda
FROM produto p
GROUP B< p.pro_st_marca
)A7ING p.pro_st_marca 3 'H$is&';
A mesma instruo poderia ser reescrita da seguinte maneira para aproveitar o ndice:
SELECT p.pro_st_marca=
A7G(p.pro_vl_maiorvenda) pro_vl_mediamaiorvenda
FROM produto p
()ERE p.pro_st_marca 3 'H$is&'
GROUP B< p.pro_st_marca;
&s%eci1ique as colunas %rinci%ais do =ndice na clusula -B&9&
Em um ndice composto a consulta apenas utilizar o ndice se as principais colunas do ndice
estiverem especificada na clausula WHERE.
Se voc possui um ndice na tabela de item nota fiscal cujas colunas so NFV_N_NUMERO e
NFV_N_NUMERO, o uso de NFV_N_NUMERO na clusula WHERE pode levar ao uso do ndice. O
mesmo no acontece se apenas NFV_N_NUMERO for utilizada. Obviamente que a utilizao das duas
colunas (de preferencia na ordem de criao do ndice) levar ao uso do ndice.
&vite a clausula OR
Se um SQL envolve OR na clausula WHERE, ele tambm pode ser reescrito substituindo o OR pelo
UNION. Voc deve verificar cuidadosamente os planos de execuo de cada SQL para decidir qual o mais
adequado e com melhor desempenho para a aplicao. O uso de OR no recomendado em aplicaes
transacionais. Seu uso muito difundido em aplicaes de 'ata3arehouse.
Cuidado com Produto CartesianoQ
Produto cartesiano a combinao de dois conjuntos, de forma que resulte em um terceiro conjunto
constitudo por todos os elementos do primeiro combinados com todos os elementos do segundo.
sso o que acontece quando voc declara em sua SQL duas tabelas ou mais e no implementa o
5oin correto entre elas. Como no exemplo abaixo:
SELECT c.cli_in_codigo
=c.cli_st_nome
=nfv.nfv_in_numero
=nfv.nfv_dt_emissao
=nfv.nfv_st_condicaopagamento
=nfv.nfv_vl_total
FROM cliente c
=nota_fiscal_venda nfv;
Elaborado por Josinei Barbosa da Silva
Pgina 148 de 154
Treinamento
No nosso caso, as tabelas possuem poucos registros, mas imagine uma situao onde existem
10.000 clientes cadastrados e sejam emitidas 50.000 notas por dia?! Teramos 500.000.000 registros
processados no banco de dados e retornados para nossa aplicao. Provavelmente essa consulta travaria o
banco de dados.
Por isso, muito cuidado para no produzir um produto cartesiano indesejado. Existem situaes em
que o produto cartesiano produzido de propsito, mas so raros . Muito raros!
S#!s com%lexas
Comandos SQLs muito complexos podem sobrecarregar o otimizador; As vezes a melhor soluo
pode ser escrever vrios SQLs simples com uma boa performance do que um nico SQL complexo. Por
exemplo: se uma juno envolve mais de 8 tabelas com uma grande quantidade de dados seria melhor
quebrar o SQL em dois ou trs SQLs menores, cada um envolvendo no mximo 4 junes de tabelas, e
guardar os resultados em tabelas temporrias criadas previamente ou criar um bloco ou funo para realizar
toda a operao, passo a passo.
O SQL no uma linguagem procedural. Usar um cdigo SQL para executar diferentes coisas
geralmente resulta em baixa performance para cada uma das tarefas. Se voc deseja que seu SQL faa
diferentes tarefas, ento escreva vrios SQLs, ao invs de escrever apenas um para fazer as diferentes
tarefas dependendo dos parmetros passados para o script.
Quando sua declarao SQL estiver se parecendo mais com um programa do que com uma instruo
de comandos, analise a possibilidade de quebr-la em pedaos.
#uando usar 8345S2 34 e &K3STS
Em vrios casos, mais de um SQL pode lhe trazer o resultado desejado. Cada um deles pode ter um
plano de execuo diferente e assim se comportar de forma diferente.
ME&%S
O operador MINUS, por exemplo, pode ser muito mais rpido do que usar WHERE NOT IN ou WHERE
NOT EXISTS. Vamos dizer que tenhamos um ndice na coluna cli_st_uf e outro ndice na coluna
cli_st_ramoatividade. Desconsiderando a disponibilidade de ndices, a consulta a seguir vai requerer
um FULL TABLE SCAN devido ao uso do predicado NOT IN:
SELECT cli_in_codigo
FROM cliente
()ERE cli_st_uf IN ('SP'= 'RT')
AND cli_st_ramoatividade NOT IN ('SUPERMERCADO'='MERCADO');
Entretanto se a mesma query for escrita da seguinte forma vai resultar em uma varredura por ndice
(NDEX SCAN):
SELECT cli_in_codigo
FROM cliente
()ERE cli_st_uf IN ('SP'= 'RT')
MINUS
SELECT cli_in_codigo
FROM cliente
()ERE cli_st_ramoatividade IN('SUPERMERCADO'='MERCADO');
ELES!S
A funo EXISTS procura pela presena de uma nica linha que satisfaz o critrio de pesquisa ao
contrrio do comando IN que procura por todas as ocorrncias. Por exemplo, vamos supor que nossa
Elaborado por Josinei Barbosa da Silva
Pgina 149 de 154
Treinamento
tabela de produtos tivesse 1000 linhas e nossa tabela de itens de nota fiscal tambm tivesse 1000 linhas.
Executando a consulta a seguir, todas as linhas da tabela de itens seriam lidas para cada linha da
tabela de produto. O Resultado final vai ser de 1.000.000 de linhas lidas:
SELECT p.pro_in_codigo
FROM produto p
()ERE p.pro_in_codigo IN(SELECT i.pro_in_codigo
FROM item_nota_fiscal_venda i
);
Se alterarmos a declarao para usar o EXISTS, no mximo, uma linha ser lida para cada linha
correspondente da tabela produto, reduzindo desta forma a sobrecarga de processamento da consulta:
SELECT p.pro_in_codigo
FROM produto p
()ERE EXISTS (SELECT 5
FROM item_nota_fiscal_venda i
()ERE i.pro_in_codigo 3 p.pro_in_codigo
);
&vite o SORT
SORT como referenciamos operaes de ordenao realizadas pelo Oracle. No ponto de vista de
desempenho, SORT nunca bom. Para resumir, o Oracle executa um SORT quando precisa arrumar os
dados antes de envi-los para o solicitante. Ele recupera os dados e executa o SORT para organiz-los da
maneira que o solicitante pediu. Por exemplo, quando necessrio devolver o resultado ordenado,
agrupado ou distinto, haver um SORT, ou seja, um ORDER BY, um GROUP BY ou um DISTINCT vai gerar
SORT.
Quando necessrio realizar essa operao, o Oracle tenta faz-lo em memria, mas se a rea
reservada para isso no for suficiente para a quantidade de dados recuperada, o Oracle vai utilizar o
TABLESPACE TEMPORRIA em disco, ou seja, vai gerar um I/O nada desejvel, pois I/O o que h de
pior para desempenho de banco de dados.
EXISTS ao invs de DISTINCT
Sempre que puder, Use o EXISTS ao invs do DISTINCT se voc desejar que o resultado contenha
apenas valores distintos ao fazer a juno de tabelas.
Por exemplo:
SELECT DISTINCT r.rep_in_codigo= r.rep_St_nome
FROM representante r=
nota_fiscal_venda n
()ERE r.rep_in_codigo 3 n.rep_in_codigo;
A declarao abaixo uma alternativa melhor:
SELECT r.rep_in_codigo= r.rep_St_nome
FROM representante r
()ERE EXISTS(SELECT n.rep_in_codigo
FROM nota_fiscal_venda n
()ERE r.rep_in_codigo 3 n.rep_in_codigo);
UNION e UNION ALL
Elaborado por Josinei Barbosa da Silva
Pgina 150 de 154
Treinamento
sempre melhor escrever SQLs diferentes para tarefas diferentes, mas se voc tem que usar apenas
um SQL, voc pode fazer ele parecer menos complexo usando o operado UNION. Toda via, o UNION tem
um problema de desempenho. O UNION executa, implicitamente um DISTINCT, ou seja, faz um SORT para
eliminar linhas repetidas.
Mas possvel executar unio de declaraes SQL evitando o SORT do UNION. Voc pode usar o
UNION ALL que retorna todas as linhas, no se preocupando com a distino entre elas. sso quer dizer
que ele no faz um SORT.
Se voc tem certeza que os resultados das SELECTs unidas no geraram linhas repetidas, utilize o
UNION ALL.
Cuidados ao utili>ar 7IE(s
Roins em 7IE(s com%lexas
Joins em VIEWs complexas no so recomendados e devem ser evitados quando possve,l
especialmente em 5oins entre duas ou mais VIEWs complexas. Quando trabalhamos com 5oins entre VIEWs
complexas, as mesmas tm que ser instanciadas primeiramente em memria e depois a consulta
efetuada em cima dos dados resultantes das VIEWs. Deu pra imaginar o custo?
9ecicla*em de 7IE(s
Esteja atento ao fato de escrever uma VIEW com um propsito e depois utiliz-la para outro onde ela
seria mal empregada. Uma consulta VIEW requer que todas as tabelas por ela referenciadas sejam
acessadas para que os dados sejam retornados. Antes de usar uma VIEW, verifique se todas as tabelas
desta VIEW precisam ser realmente acessadas apara retornar os dados que voc precisa. Seno, no
utilize a VIEW. Ao invs disso use as tabelas base ou crie uma nova VIEW (se necessrio) para sua
necessidade em especifico.
Database !inH
As vezes sua consulta est bem elaborada, o banco possui ndices seletivos e tudo mais que tenda
para a boa performance, mas a sua consulta demora para dar retorno.
sso pode ser causado por causa de 'atabase Link.
Se a sua consulta acessa um objeto de outro banco de dados (diretamente ou por sinnimo), voc
pode ter um problema de performance, pois existe o tempo rede entre um banco de dados e outro (alm do
trfego de rede do banco de dados base com a sua aplicao).
Como se no bastasse o fato de existir um trfego de rede extra, a performance de Database Link
no das melhores.
Os Database Links so recomendados apenas para carga de dados em processos agendados (jobs
noturnos, por exemplo).
'%licativos versus Banco de Dados
As aplicaes devem tentar acessar os dados apenas uma vez. sto reduz o trafego de informaes
na rede alm da carga no banco de dados. Considere as seguintes tcnicas:
5tili>e o comando C'S& %ara combinar mMlti%las varreduras+
Elaborado por Josinei Barbosa da Silva
Pgina 151 de 154
Treinamento
sso possvel movendo a condio WHERE de cada varredura em um comando CASE,
que filtra os dados de cada agregao. Eliminando n-1 varreduras, pode representar um grande
ganho de performance. O exemplo a seguir que saber o numero de representantes cuja maior
venda menor do que 20000, entre 20001 e 40000, e maior que 40000 todo ms. sto pode ser
feito atravs de 3 consultas:
SELECT COUNT(*)
FROM representante r
()ERE r.rep_vl_maiorvenda @3 46666;
SELECT COUNT(*)
FROM representante r
()ERE r.rep_vl_maiorvenda BET(EEN 46665 AND 96666;
SELECT COUNT(*)
FROM representante r
()ERE r.rep_vl_maiorvenda A 96666;
Neste caso executamos trs comandos para conseguir as informaes e conseguimos
com sucesso, entretanto, mais eficiente rodar toda consulta como um nico SQL. Cada
nmero calculado como uma coluna. O count usa um filtro com o comando CASE para
contabilizar apenas as linhas onde a condio valida, como mostrado abaixo:
SELECT COUNT (CASE ()EN r.rep_vl_maiorvenda @3 46666
T)EN 5 ELSE NULL END) menor_20000=
COUNT (CASE ()EN r.rep_vl_maiorvenda BET(EEN 46665 AND 96666
T)EN 5 ELSE NULL END) entre_20000_40000=
COUNT (CASE ()EN r.rep_vl_maiorvenda A 96666
T)EN 5 ELSE NULL END) maior_40000
FROM representante r;
Teremos as mesmas informaes com um nico acesso ao banco e sem prejudicar o
plano de execuo!
5tili>e blocos !"S#! %ara executar o%eraes S#! re%etidas em !OOs de sua
a%licao
magine que voc acaba de incluir uma nota fiscal com 200 itens e para cada item, voc
precisa validar alguma coisa no cadastro de produto? Voc ter que executar 200 vezes a
mesma SELECT mudando apenas o produto, ou seja, duzentas vezes esse comando ser
enviado pela rede ao servidor e o resultado ser devolvido. Agora imagine que voc emita 400
notas por dia! Que trfico de rede, no?!
Existem maneiras de evitar esse problema!
Muitas linguagens possuem recursos para que voc fornea um array como parmetro
para o bloco SQL e ento processe as instrues de cada linha do array antes de retornar
aplicao. No nosso exemplo, o array conteria os itens da nota e as instrues seriam as
SELECTs de verificao.
No Delphi, por exemplo, existe um conjunto de componentes especficos para acesso
banco de dados Oracle (no gratuito) que disponibiliza um tipo chamado TPLSQLTable. Voc
pode definir uma varivel desse tipo, aliment-lo com os itens que desejar e ter um array em
Delphi. O seu bloco PL/SQL s precisa ter uma coleo do tipo tabela por ndice e receber o
array do Delphi como parmetro, como abaixo:
DECLARE
Elaborado por Josinei Barbosa da Silva
Pgina 152 de 154
Treinamento
-- Definir um vetor para conter itens
T<PE TCodigoProduto IS TABLE OF produto.pro_in_codigo;T<PE INDEX B<
Bin$&QFintL&;
-- Declarao de variveis
cCodigoProduto TCodigoProduto;
BEGIN
-- Coleo PL/SQL recebendo como parmetro array do Delphi com itens da nota
-- (pCodigoProduto o array em Delphi e cCodigoProduto o array em PL/SQL)
cCodigoProduto :3 :pCodigoProduto;
-- Executa loop na lista de itens
FOR vCount IN cCodigoProduto.FIRST..cCodigoProduto.LAST LOOP

/* Processamento de cada linha do array montado no Delphi*/

END LOOP;
END;
1
5se a clausula 9&T59434L em declaraes D8!+
Quando apropriado, use INSERT, UPDATE ou DELETE com a clusula RETURNING para
selecionar e modificar dados com uma nica chamada. Esta tcnica aumenta a performance
diminuindo o numero de chamadas ao banco de dados.
O RETURNING pode te retornar um dos valores que foram alterados. Por exemplo, voc
inclui uma nova nota fiscal em seu sistema e aps a gravao precisa informar o nmero da
nota gravada para o usurio. Ao invs de executar uma SELECT depois do INSERT s para
recuperar esse nmero, voc inclui no seu INSERT a clausula RETURNING para retornar o valor
gravado na coluna de nmero.
Elaborado por Josinei Barbosa da Silva
Pgina 153 de 154
Treinamento
&xerc=cios ro%ostos
1. Revise todos os procedimentos criados nos pacotes pck_cliente, pck_produto e
pck_representant, aplicando as boas prtica de desenvolvimento SQL.
Elaborado por Josinei Barbosa da Silva
Pgina 154 de 154

Você também pode gostar