Você está na página 1de 93

BOAS PRÁTICAS DE PROGRAMAÇÃO EM

AMBIENTE ORACLE

Versão Data Autor


1.0 16/5/96 Oracle do Brasil.
ÍNDICE
1- SQL - Conceitos e guia de utilização_________________________________________
1.1- Fazes do processo de resolução de uma query___________________________________
1.2- Otimizador________________________________________________________________
1.2.1 - RULE________________________________________________________________________
1.2.2- COST________________________________________________________________________
1.2.3- Como definir o modo de otimização________________________________________________
1.2.4- Passos do Otimizador____________________________________________________________
1.2.5- Resolução dos joins pelo otimizador________________________________________________
1.3- Uso de Índices_____________________________________________________________
1.4- Escrevendo seu SQL de modo Ótimo__________________________________________
1.4.1- Lista de colunas________________________________________________________________
1.4.2- Cláusula from__________________________________________________________________
1.4.3- Cláusula where_________________________________________________________________
1.4.4- Cláusula group by_______________________________________________________________
1.4.5- Cláusula having________________________________________________________________
1.4.6- Cláusula order by_______________________________________________________________
1.4.7- Alternativas de sintaxe para mesmo resultados________________________________________
1.5- Aplicações com problemas de performance_____________________________________
1.5.1- Explain Plan___________________________________________________________________
1.5.2- Tkprof/Sql Trace________________________________________________________________
1.6- Database Hints____________________________________________________________
1.6.1- Hints disponíveis para o desenvolvedor:_____________________________________________
1.7- O Conceito de transação____________________________________________________
1.7.1- Cuidados no uso da transação_____________________________________________________
1.7.2- Locks_________________________________________________________________________
1.7.3- Dead Locks____________________________________________________________________
2- PL/SQL - Conceitos e guia de utilização_____________________________________
2.1- O Motor do PL/SQL________________________________________________________
2.1.1- Tools PL/SQL (1.0)_____________________________________________________________
2.1.2- Server PL/SQL (2.0)_____________________________________________________________
2.1.3- Considerações comuns___________________________________________________________
2.2- Uso de variaveis____________________________________________________________
2.2.1- Conversão de tipos______________________________________________________________
2.2.2- %TYPE e %ROWTYPE__________________________________________________________
2.3- Estruturação de programas__________________________________________________
2.3.1- OVERLOAD___________________________________________________________________
2.3.2- Storage Procedures e functions____________________________________________________
2.3.3- Estrutura de Packages____________________________________________________________
2.3.4- Packages Pré-Definidas__________________________________________________________
2.4- Cursores__________________________________________________________________
2.5- Recursividade_____________________________________________________________
2.6- Padrão de codificação de programas PL/SQL___________________________________
2.6.1- Ativação de spool em ambientes SQL*PLUS_________________________________________
2.6.2- Cabeçalho do programa__________________________________________________________
2.6.2- Declaração foward de todas as procedures existentes___________________________________

2
2.6.3- Funções e procedures envolvidas em ordem alfabéticas_________________________________
2.6.4- Corpo do programa e procedures___________________________________________________
2.6.5- Padrão de nome de variáveis______________________________________________________
2.6.6- Commit explicito ao termino do programa___________________________________________
2.6.7- Retorno de SQL.SQLCODE em ambientes SQL*PLUS_________________________________
2.6.8- Exemplo de fonte padronizado_____________________________________________________
3-FORMS 4.0 - Conceitos e guia de utilização___________________________________
3.1- Características da ferramenta________________________________________________
3.1.1- Divisão do processamento entre ambientes___________________________________________
3.1.2- Oracle.ini______________________________________________________________________
3.1.3- Codificação de eventos___________________________________________________________
3.2- Modularização de forms e o código PL/SQL____________________________________
Observações:________________________________________________________________________
3.3- Localização de procedimentos PL/SQL________________________________________
3.3.1- Procedures definidas a nível de forms_______________________________________________
3.3.2- Definidas a nível de lib externas___________________________________________________
3.3.3- Nível de base___________________________________________________________________
3.5- Uso de Variáveis___________________________________________________________
3.5.1 - Globais_______________________________________________________________________
3.5.2 -Canvas Null____________________________________________________________________
3.5.3- Internas de PL__________________________________________________________________
3.5.4- Parâmetros_____________________________________________________________________
3.6- Obtenção de Data/Hora_____________________________________________________
3.7- Lista de valores (lov)________________________________________________________
3.8- Triggers mais importantes - Observações______________________________________
3.8.1-Key-<TECLA>_________________________________________________________________
3.8.2- Key-others_____________________________________________________________________
3.8.3- Do-key________________________________________________________________________
3.8.4 Post-change____________________________________________________________________
3.8.5- When-new-???-instance__________________________________________________________
3.8.6- When-validate-item_____________________________________________________________
3.8.7- When-validate-record____________________________________________________________
3.8.8- Post-query_____________________________________________________________________
3.8.9- Pre-query______________________________________________________________________
3.8.10- Pre/Post-????? (Transação)______________________________________________________
3.9 - Integração com base de dados_______________________________________________
3.9.1- Fetch, Buffer e Displayed_________________________________________________________
3.9.2- Base Table vs. Processamento manual______________________________________________
3.9.3- Relação Master-Detail___________________________________________________________
3.9.4- Modo ENTER QUERY__________________________________________________________
3.9.5- Blocos base-table baseado em view’s_______________________________________________
3.9.6- Ativando o SQL TRACE_________________________________________________________
3.10- Integração com outros forms________________________________________________
2.11 -Lock’s no Forms__________________________________________________________
3.12- Forms em modo character__________________________________________________
3.13 - Bugs Comuns no ambiente FORMS._________________________________________
3.13.1- Campo com VARCHAR2 muito grandes___________________________________________

3
3.13.2- Library quando aberta em modo read-only__________________________________________
3.13.3- GPF alterando radio groups______________________________________________________
3.13.4- Sinonimos de database link______________________________________________________
3.13.5- RAISE_APLICATION_ERROR em STORED PROCEDURES._________________________
3.13.6- LOVs sem espaco em disco______________________________________________________
3.13.7- ROS-201 Salvando FORMS em disco______________________________________________
3.13.8- Parametros via run_product em maiúsculas_________________________________________
3.13.9- Refresh de objetos no forms designer______________________________________________
4-REPORT 2.0 - Conceitos e guia de utilização__________________________________
4.1- DATA MODEL e Queries___________________________________________________
4.1.1- Defina o mínimo de queries possiveis_______________________________________________
4.1.2- Utilização de parâmetros léxicos___________________________________________________
4.1.3- Utilização de record-group________________________________________________________
4.2- Processamento de eventos dentro do report_____________________________________
4.3- Considerações gerais na criação de um report.__________________________________
4.4- Monitorando performance___________________________________________________
4.4.1- SQL TRACE___________________________________________________________________
4.4.2- PROFILE_____________________________________________________________________
4.5- Report Character Mode_____________________________________________________
4.6- Client ou Server ?__________________________________________________________
4.7- Principais bug’s no ambiente report.__________________________________________
4.7.1- GPF causados por falta de espaço em disco.__________________________________________
4.7.2- Loops infinitos detectados________________________________________________________
4.7.3- Problemas de impressão - Áreas Negras_____________________________________________
4.7.4- Problemas de impressão - Imagens_________________________________________________
5- Prevenção de GPF’s_____________________________________________________
5.1- Como minimizar a utilização de memória/recursos do windows.___________________
5.2 - O que fazer se um GPF acontecer____________________________________________
5.3- Se o erro for de ambiente___________________________________________________
5.4- Se o erro for da aplicação____________________________________________________
6- Homologando um aplicativo Oracle_________________________________________
6.1- Analise da Lógica utilizada__________________________________________________
6.1.1- Processamento convencional vs. Pensar relacionalmente._______________________________
6.1.2- Verificando transações.__________________________________________________________
6.1.3- Lógica de restart._______________________________________________________________
6.1.5- Sempre verificar execução de comandos foi bem sucedida.______________________________
6.1.6- Aplicando os conceitos___________________________________________________________
6.2- Classificando as aplicações__________________________________________________
Tipo A - Normal_____________________________________________________________________
Tipo B - Importantes__________________________________________________________________
Tipo C - Criticas_____________________________________________________________________
6.3- Verificando conceitos aplicados_______________________________________________
6.3.1- Estrutura básica das fichas:_______________________________________________________
6.4- Definindo tempos Aceitáveis_________________________________________________

4
Exemplo de Ficha de Medição de Tempos - Aplicativos Oracle_______________________________
6.5- Ferramenta PECS de acompanhamento de testes._______________________________
6.5.1 - O que é o PECS________________________________________________________________
6.5.2- Benefícios de uso do PECS_______________________________________________________
6.5.3- Componentes e definições do PECS.________________________________________________
6.5.4- Coletando informações sobre eventos do FORMS_____________________________________
6.5.5- Eventos específicos da Aplicação__________________________________________________

5
1- SQL - CONCEITOS E GUIA DE UTILIZAÇÃO
1.1- FAZES DO PROCESSO DE RESOLUÇÃO DE UMA QUERY

Server

1- A Aplicação envia o comando SQL ao server para a resolução do mesmo (1).


2- Tem inicio a fase do PARSE (2). Primeiramente nesta fase é verificado na Sql Área se o comando já foi
resolvido anteriormente (3) caso não seja encontrado inicia-se o processo do parse. É verificado no
dicionário do database (4) se existem tabelas, colunas, permissões para o acesso dos objetos. A sintaxe
dos comandos é validada e caso o otimizador esteja em modo COST verifica se estatísticas estão
armazenadas no dicionário de dados (pelo comando analyze table compute estatistics) de modo a que
o otimizador inicie o processo de resolução do plano de acesso aos dados. Após o processo o plano de
acesso é inserido na Sql Área para reutilização no caso de um novo parse.
3- Com o caminho resolvido inicia-se a fase EXECUTE onde o comando é realizado. Esta fase executa as
leituras dos blocos necessários do Database (através do cache Database Buffer Cache) e gravações no
Database. Para isso são utilizados áreas temporárias (7) para armazenamento de tabelas resultados e
áreas de sort.
4- Caso o comando seja um select, isto é retorne algum dado, tem inicio a fase final de fetch (8), onde o
resultado é enviado a aplicação que enviou o comando.

Considerações.
A fase mais importante é a do parse, nela são gastos mais recursos para validação do comando e definição do
plano de acesso, quando um comando SQL é emitido pela segunda vez, este processo já está “resolvido”
economizando-se grande parte do processamento, uma das metas ao se desenvolver os aplicativos é a boa
utilização da SQL Área de modo a evitar todo este processamento.

6
1.2- OTIMIZADOR

No Oracle Server o processo do optimizer é o responsável pela definição do plano de acesso aos dados
requisitados pela query. É a “inteligência” do Oracle , o responsável pela boa performance ou não de sua query.
Existem basicamente 2 modos para o optimizer atuar:

1.2.1 - RULE

Este método é o antigo método utilizado pelo Oracle 6 e 7.0 e agora opcional na 7.1 , baseia-se em determinar
os planos de acessos possíveis e baseado em uma escala de regras definir qual o melhor dos planos de acesso
deva ser utilizado.

As regras em ordem de preferencia são:

1. Acesso via ROWID


2. Acesso via Cluster Join (uma única row)
3. Acesso via Hash Cluster Key com Unique ou Primary Key (Uma única row)
4. Acesso via índice Unique ou Primary Key (Uma única row )
5. Clustered Join
6. Hash Cluster Key
7. Cluster Key indexada
8. Índice composto (usando todas as colunas)
9. Índice de uma coluna (um ou vários)
10. Colunas de um índice composto
11. Faixa em um índice ( >= ou <=)
12. Sort Merge Join
13. MAX ou MIN de colunas indexadas
14. ORDER BY em colunas indexadas
15. Full Table Scan ( pesquisa seqüencial na tabela)

Este método não leva em consideração a natureza do dado em si. Caso um dos caminhos possíveis ao dado se
enquadre numa regra de menor nível , este será utilizado pelo otimizador. Deste modo se existir um índice para
a tabela ele sempre será utilizado mesmo que resulte num maior tempo de acesso.

Não é aconselhável setar esta preferência de “optimizer” em detrimento da opção COST.

1.2.2- COST

Esta opção está funcional nas releases do Oracle 7.1 em diante, na release 7.0 existe um bug e a mesma não
deve ser utilizada. O Otimizador calcula as opções de acesso ao dado e utiliza o seu “conhecimento” do dado
para escolher qual o melhor plano de acesso, baseado na tabela acima porem observando a seletividade do
dado, numero de blocos necessários para se fazer um full table scan vs. numero de blocos lidos numa leitura,
números de linhas na tabela e outras estatísticas geradas pelo comando analyze.

É importante que se rode as estatísticas de modo que as informações coletadas reflitam a realidade do
dado que será utilizado pelo programa em produção, o comando analyze deve ser executado
periodicamente nas tabelas acessadas.

7
1.2.3- Como definir o modo de otimização

a) No parâmetro OPTIMIZER_MODE do init<instance>.ora com os parâmetros CHOSE,RULE,COST, onde


CHOSE é o default do banco de dados

b)Utilizando-se o comando ALTER SESSION SET OPTIMIZER_GOAL=<opção> onde opção pode assumir
os valores CHOSE, RULE, ALL_ROWS e FIRST_ROWS, sendo:

· CHOSE - O otimizador verifica se existem estatísticas no dicionário de dados, caso existam utiliza o
método COST, se não utiliza o método RULE.
· ALL_ROWS - É o método “default” do otimizador pôr custo, a meta é que a query toda seja resolvida no
menor espaço de tempo
· FIRST_ROWS - Utiliza o método custo também, porem a meta é ter a resposta inicial no menor intervalo de
tempo em detrimento da resolução da query como um todo.
· RULE - Utiliza o método RULE para resolução do otimizador.

Observações.
1) Dentro da opção COST é possível alterar o comportamento do otimizador utilizando-se dos database hints
vistos a frente deste documento.
2) O comando ALTER SESSION só altera o otimizador para a sessão corrente do usuário, em uma nova
conexão volta o valor default do init.ora.

1.2.4- Passos do Otimizador

As etapas seguintes são a ordem que o processo de otimização segue na resolução do processo de PARSE. Esta
ordem é um processamento interno, não podendo ser modificado pelo desenvolvedor.

A) Resolução de expressões e condições com constantes.

Expressões constantes são resolvidas, ex.:


WHERE VL_SAL < 2500 * 1.5
para
WHERE VL_SAL < 3750

B) Transformação de comandos

Comandos são transformados em comandos mais simples e eficientes. Subqueries são transformadas em
joins, condições OR são transformadas em union e através de transitividade restringindo consultas antes
de aplicar o join. Ex.:
Select movimento.cod_venda, comissao.tax_comissao from
movimento, comissao
where movimento.cod_venda < 200 and
movimento.cod_venda = comissao.cod_venda;

é transformado em
Select movimento.cod_venda, comissao.tax_comissao from
movimento, comissao
where movimento.cod_venda < 200 and
comissao.cod_venda < 200 and
movimento.cod_venda = comissao.cod_venda;

O Otimizador deduz pela transitividade que comissão.cod_venda < 200 .

8
C) Merge de SQL de views com o SQL analisado

Caso alguma das tabelas acessadas pelo comando seja uma view, o comando SQL é unificado com o
comando implícito na view, caso isto não seja possível o otimizador resolve a view e utiliza as linhas de
resultado como se fosse uma tabela.

D) Escolha do método de otimização

Baseado nos parâmetros ou em hints é definido qual o método de otimização será utilizado.

E) Obtenção dos caminhos de acesso disponíveis para as tabelas envolvidas

Otimizador resolve o melhor caminho para acessar cada tabela envolvida.

F) Definição da ordem de resolução dos joins

Dentro da lista do “from” o otimizador define a ordem em que os joins serão resolvidos, um par de tabela
é resolvido e o resultado é “joined” com a próxima tabela. A ordem em que os pares são resolvidos é
determinada pelo otimizador baseado nas estatísticas das tabelas e no score dos caminhos de acesso
disponíveis. Caso o desenvolvedor possua alguma informação sobre o número de linhas que será
retornado e esta informação não esteja disponível para o otimizador é possível indicar a ordem em que
as tabelas serão “joined” via hint ORDERED.

G) Escolha do modo de resolução dos joins

Para cada par o otimizador define o tipo de join a ser usado:

1. Nested Loops

Otimizador escolhe uma tabela para tabela drive “outer table” e a outra se torna a tabela “inner”, para
cada linha da tabela drive o Oracle seleciona as linhas que satisfaçam na tabela inner.

2. Sort Merge

Oracle executa um sort das duas tabelas pela chave de ligação do “where”, após o sort é executado um
“merge” das 2 tabelas de modo a se obter as linhas resultantes.

3. Cluster Join

É utilizado em equi-joins de tabelas que estejam localizadas num mesmo Cluster. Num cluster as
tabelas são fisicamente armazenadas seguindo a cluster-key de modo a facilitar o join. É basicamente um
Nested Loop onde a tabela inner já está fisicamente intercalada entre as rows da tabela drive.

1.2.5- Resolução dos joins pelo otimizador

Na resolução por regra, o otimizador gera N combinações possíveis de join order e métodos de join usando um
algoritmo interno, dentro deste universo de planos de acessos ele decide qual usar tendo como meta a melhor
utilização de nested loops e sorts merge, para isso ele analisa as opções baseada no ranking das operações
necessárias e nos métodos de acesso das tabelas, quando o método usado é o de custo as estatísticas e
composição dos dados nas tabelas também é levado em conta.

9
Observação:

Quando o otimizador encontra um outer join , a tabela com o operador (+) deve se situar depois da tabela
referenciada na igualdade. O otimizador impõe esta regra na hora de definir a ordem do join.

1.3- USO DE ÍNDICES


Índices são estruturas de dados baseados em árvore-b contendo chaves de acesso e endereços para as
linhas de uma tabelas. Eles diminuem o ranking de acessos no otimizador por regra e normalmente
diminuem o custo dentro da opção por custo. Nem sempre o acesso pelo índice incrementa a performance
de seu comando, as vezes um full-table scan é mais eficiente dependendo do tamanho da tabela.
Simplificando o processo podemos admitir que na melhor das hipóteses um acesso pelo índice requer
uma leitura na pagina de índices e uma leitura na página de dados, isto significa que qualquer acesso pelo
índice no mínimo requer 2 leituras, ou seja , 2 blocos de 4K num total de 8K de informações lidas. Com
8K de informações pode se num full-table scan acessar numa área continua mais de 150 linhas de uma
tabela com tamanho de registro de 50 posições.

Deste modo grande parte das tabelas tipo código, descrição o uso do índice não é eficiente. O otimizador
por custo leva em consideração o numero de linhas da tabela e o por regra sempre usa índice.

Não existe um número absoluto de linhas retornadas que possa indicar se o uso do índice é vantagem ou
não, normalmente em torno de 15% este numero depende de vários fatores como o tamanho do bloco em
que o database foi criado, o numero do parâmetro DB_FILE_MULTIBLOCK_READ_COUNT, e o tamanho
que um registro típico ocupa no bloco. O melhor procedimento é tendo criado estatísticas no banco deixar a
cargo do otimizador por custo a decisão no geral e em aplicações onde venha a se detectar problemas de
performance efetuar uma analise mais detalhada com o tkprof e explain plan.

Índices são obrigatórios no caso de se querer garantir a unicidade da linha através de constraint UNIQUE ou
PRIMARY KEY (criados automaticamente na criação das constraints).

É necessário ter em mente que ao mesmo tempo que índices aumentam a performance de consultas, degradam a
performance em inserções, atualizações e deleções. Para qualquer destas operações efetuada na tabela será
necessário atualização de cada índice envolvido na linha/coluna. Tomar cuidado na criação de índices em
tabelas muito dinâmicas, deve-se analisar o custo vs. beneficio em relação a consultas vs. atualizações.

Deve se criar índices pelas foreignkeys de uma tabela, alem de auxiliar o processo de join evita-se que um share
lock seja efetuado na tabela pai, isto evita que inserções, alterações e deleções sejam proibidas na tabela pai
enquanto houverem transações pendentes na tabela filho .

Na criação de índices compostos tenha em mente que

1.3.1 Se um select requerer somente colunas do índice composto, o otimizador não necessita ir a
página de dados.

1.3.1 O Índice é usado caso somente parte das colunas indexadas apareçam no where, contanto que
sejam as primeiras colunas do índice.
Ex. Índice cuja as colunas indexadas sejam (col1, col2 e col3)
where col1= 10 (Índice usado)
where col1 = 10 and col2 = 30 (Índice usado)
where col1 = 10 and col2 = 30 and col3=20 (Índice usado)
where col1= 10 and col3 = 40 (Índice é parcialmente usado)
where col2= 20 and col3 = 40 (Índice não é usado)

1.3.1 Nas escolha de qual coluna deve ser utilizado primeiro escolha a coluna mais
freqüentemente utilizada em where

10
1.3.1 Caso as colunas tenham freqüência comum nos where utilize na definição da ordem de
criação das colunas as colunas mais seletiva para a menos seletiva onde uma
coluna com grande seletividade é aquela que retorna poucas linhas para cada
valor distinto. Uma primary key por exemplo tem a maior seletividade possível
já que somente uma linha existe para cada valor que a coluna possa conter.

1.3.1 Qualquer operação ou função aplicada em uma coluna de índice inibe este índice.

1.4- ESCREVENDO SEU SQL DE MODO ÓTIMO


Pode-se escrever o seu comando sql de modo a minimizar o trabalho do otimizador e melhorar tanto a
performance quanto a clareza de seu comando.

Tendo como padrão um comando SQL

SELECT <LISTA DE COLUNAS>


FROM <LISTA DE TABELAS>
WHERE <CONDIÇAO>
GROUP BY <COLUNAS>
HAVING <CONDICAO>
ORDER BY <COLUNAS>

Podemos ter certos padrões em cada uma das áreas do comando.

1.4.1- Lista de colunas

A) Sempre qualifique as colunas quando mais de uma tabela for envolvida.


Num Join uma coluna deve ser obrigatoriamente qualificada ( preceder o nome da tabela antes) em uma
cláusula select quando a mesma coluna existir em mais de uma das tabelas da cláusula from, deve se
qualificar a coluna mesmo que esta só apareça em uma das tabelas, isto evita o trabalho do otimizador de
checar em todas as tabelas a fim de evitar possível erro. Ex.

Transformar
Select cd_func,nm_func, nm_departamento
from func, dept
where cd_dept_func = cd_dept
para
Select func.cd_func,func.nm_func, dept.nm_departamento
from func, dept
where func.cd_dept_func = dept.cd_dept

B) Criar apelidos (alias) para expressões


Ajuda no entendimento do comando e em futuras declarações de cursor. Ex.
Select cd_mat, vl_sal , vl_sal * decode(IN_GER,’S’,1.15,1.20) vl_sal_proposto
from func.

11
C) Evite DISTINCT
uso do predicado DISTINCT provoca um sort na tabela resultado de modo a se retirar as linhas
coincidentes, procure utilizar as estruturas de dados com primary key para obter os resultados únicos
desejados .

D) Evitar operações dentro de funções de coluna.


Transformar
Select sum(vl_sal * 1.15)
from func
para
Select sum(vl_sal) * 1.15
from func

1.4.2- Cláusula from

A ordem em que as tabelas aparecem não é muito significativa para o otimizador, tanto o otimizador por
regra quanto o por custo testam varias combinações antes de decidir por algoritmos internos qual a
melhor ordem de resolução por joins. Somente em ultimo caso leva-se em consideração a ordem em que
as tabelas se apresentam.

Em sua definição da clausula FROM apresente as tabelas na ordem das tabelas com mais linhas para as
com menos linhas. Ex.

Select .... from tab1 , tab2 , tab3 where .......

Onde tab1 tem mais linhas que tab2


tab2 tem mais linhas que tab3.

Entende-se tabela com mais linhas o subconjunto de linhas recuperadas já aplicados os filtros explícitos
na condição where.

1.4.3- Cláusula where

A) Posicionamento dos predicados


Organize sua cláusula where de modo a ser visível a lógica utilizada, agrupe primeiramente as condições
de ligação entre tabelas e depois as condições de filtros individuais. Normalmente o parse começa a
resolver a cláusula where de baixo para cima . Ex.

SELECT func.cd_mat, func.nm_func, dept.nm_depto, proj.nm_projeto


FROM func, dept , alocacao , proj
WHERE
func.cd_depto = depto.cd_depto AND -- Ligação com Joins
func.cd_mat = alocacao.cd_mat AND
alocacao.cd_proj - proj_cd_proj AND
func.in_sexo = ‘M’ AND - - Filtros
dept.cd_dept_contabil = ‘13’ AND
proj.dt_termino < SYSDATE

B) Se possível não aplique funções/Expressões em colunas da base


Quando uma expressão ou função é aplicado numa coluna de índice o mesmo não é utilizado.

12
Substitua:

TO_CHAR(dt_adm,’DD/MM/YY’) = ‘01/04/96’ por dt_adm = TO_DATE(‘01/04/96’,’DD/MM/YY’)


vl_sal * 1.5 < 2000 por vl_sal < 2000 / 1.5

C) Informe todas as ligações entre tabelas


No caso de se usar mais de 2 tabelas num join “amarre” as tabelas de todas as formas possíveis, mesmo
que aparentemente não seja necessário.

SELECT tab1.campo1 , tab2.campo2, tab3.campo3


FROM tab1 , tab2 , tab3
WHERE tab1.campfk1 = tab2.campk and
tab2.campfk = tab3.campk

Se tab1 tiver a foreignkey para tab3 inclua na condição de where.

SELECT tab1.campo1 , tab2.campo2, tab3.campo3


FROM tab1 , tab2 , tab3
WHERE tab1.campfk1 = tab2.campk and
tab2.campfk = tab3.campk and
tab1.campfk2 = tab3.campk

Isto permite ao otimizador checar se efetuando o join tab1, tab3 antes dará um custo menor para a query.
O conceito de transitividade que o otimizador executa só funciona entre colunas e expressões/constantes,
não sendo possível de coluna para coluna.

D) Uso do Like
uso do comando like pode não utilizar índices.

NM_FUNC LIKE ‘JOSE’ é substituido por NM_FUNC = ‘JOSE’


NM_FUNC LIKE `JOSE%’ utiliza índice caso a coluna NM_FUNC seja indexada.
NM_FUNC LIKE ‘%JOSE%’ não utiliza índice.

E) Uso de colunas que existam em índices compostos


Procure utilizar sempre toda a chave de um índice composto ou pelo menos as chaves iniciais do mesmo
na cláusula where, o Otimizador não utilizara o índice caso falte alguma coluna mais a esquerda da
chave. Verifique a composição do índice e a disponibilidade de completa-lo ( A informação é disponível
porem não estava sendo utilizada) .

Vide item 1.1.3 deste documento.

13
F) Forçando o não uso de um índice
Pode se forçar que o otimizador não use o índice sobre determinada coluna aplicando se uma expressão
sobre a mesma. Ex.

CD_MAT < ‘1000’ para CD_MAT || ‘’ < ‘1000’


DT_ADM < SYSDATE - 30 para DT_ADM + 30 < SYSDATE

Vide item 1.3 deste documento

G) IS NULL
Campos NULL não são armazenados nos índices, de modo que uma condição CAMPO IS NULL não
utilizara o índice para pesquisa forçando um full-table scan.

H) Use parênteses
Isto facilita a compreensão da lógica em expressões boleanas que utilizem AND e OR, evitando erros e
facilitando o processo de parse.

I) Not = (not equal)


A cláusula CAMPO NOT - VALOR não utiliza o índice.

J) ROWID
Rowid é uma pseudo coluna que toda tabela tem cujo valor é o endereço físico da linha dentro da tabela,
seu uso na cláusula where oferece a melhor performance possível (caso o valor seja conhecido na sessão).

Deve se observar que o rowid não deve ser armazenado para ser usado posteriormente, numa
restruturação da tabela via import/export este valor pode ser alterado.

Exemplo:

DECLARE
..
CURSOR c1 IS SELECT cd_mat, dt_adm, ROWID FROM func;
reg c1%ROWTYPE

BEGIN
OPEN c1;
FETCH c1 INTO reg; /* Obtenho o ROWID para futuro acesso */
..
..
UPDATE func SET vl_sal = vl_sal * 1.5 WHERE rowid = reg.rowid; /* Uso o Rowid como acesso */

CLOSE c1;
END;

14
1.4.4- Cláusula group by

· Ao utilizar a cláusula group by toda coluna que aparecer na lista de colunas selecionadas deve constar
também da cláusula group by com exceção das funções de coluna.

· A Cláusula group by implica em um sort.

1.4.5- Cláusula having

· Só aplique na clausula having restrições em cima de colunas calculadas via funções de coluna ( sum,
max, min, avg, etc..), utilize a clausula WHERE para qualquer outro filtro.

Ex. Troque
SELECT cd_depto, count(*)
FROM func
GROUP BY cd_depto
HAVING COUNT(*) < 10 AND
cd_depto <> ‘12’

para
SELECT cd_depto, COUNT(*)
FROM func
WHERE cd_depto <> ‘12’
GROUP by cd_depto
HAVING COUNT(*) < 10

1.4.6- Cláusula order by

· Índice só é usado quando estiver nas colunas pertencentes a drive table no caso de um join.

· Não são aplicados quando usados em select que envolvam union, union all, minus e intersect.

· A Partir da versão 7.1 é permitido o uso de apelidos de colunas na clausula order by

· O uso do order by é obrigatório para se garantir que o resultado da tabela venha ordenado, mesmo que
exista um índice para a tabela e este esteja sendo utilizado.

15
1.4.7- Alternativas de sintaxe para mesmo resultados

Várias alternativas de comando são possíveis para se obter um mesmo resultado, o otimizador já troca
vários comandos de forma a obter um melhor resultado. Ex.

De Para
a between b and c a >= b and a <=c
a in ( b,c,d) a=b or a=c or a=d
not in exists
subselect join

É interessante que o desenvolvedor procure já utilizar estas alternativas para simplificar o trabalho do
otimizador.

Exemplos:

Subselect para join

De: Para:
SELECT cd_func, nm_func SELECT func.cd_func, func.nm_func
FROM func FROM func, dept
WHERE cd_dept IN ( SELECT cd_dept FROM dept WHERE WHERE func.cd_dept = dept.cd_dept AND
nm_depto LIKE ‘%DIRET dept.nm_depto LIKE ‘%DIRET%’)
%’ )

De: Para:
SELECT cd_func, nm_func SELECT cd_func, nm_func
FROM func A FROM func A, FUNC B
WHERE EXISTS ( SELECT * FROM func B WHERE WHERE A.cd_func = B.cd_gerente )
B.cd_gerente = A.cd_func )

Not In para Not Exists

De: Para: (usa índice)

Select cd_func, nm_func Select func.cd_func, func.nm_func


from func from func where not exists ( Select * from dept where
where cd_dept not in ( select cd_dept from dept where func.cd_dept = dept.cd_depto and
nm_depto like ‘%DIRET%’ ) nm_depto like %DIRET%’)

1.5- APLICAÇÕES COM PROBLEMAS DE PERFORMANCE


Entendendo como o otimizador funciona permite que se guie a condição de where para uma melhor
utilização dos índices e melhor resultado de uma query. Caso uma aplicação tenha problema de
performance e a primeira vista as construções dos comandos estejam coerentes com as regras
apresentadas acima, deve se passar a uma analise mais detalhada do que esta acontecendo com a query.

Existem 2 ações que podem ser tomadas para analisar o caminho que o otimizador escolheu para a
resolução de uma query, o EXPLAIN PLAN para analisar queries individuais e os utilitário
TKPROF/TRACE.

16
1.5.1- Explain Plan

Explain plan é um comando que mostra o plano de acesso utilizado por um único comando sql. Para
utiliza-lo é necessário a criação de uma tabela PLAN_TABLE na conta do desenvolvedor que fará a
utilização do comando.

create table PLAN_TABLE (


statement_id varchar2(30),
timestamp date,
remarks varchar2(80),
operation varchar2(30),
options varchar2(30),
object_node varchar2(128),
object_owner varchar2(30),
object_name varchar2(30),
object_instance numeric,
object_type varchar2(30),
optimizer varchar2(255),
search_columns numeric,
id numeric,
parent_id numeric,
position numeric,
other long);

EXPLAIN PLAN
SET STATEMENT_ID = <NOME> INTO PLAN_TABLE FOR
<SQL a ser analisado> ;

Exemplo de analise via explain:

EXPLAIN PLAN
SET STATEMENT_ID = 'Emp_Sal'
FOR SELECT ename, job, sal, dname
FROM emp, dept
WHERE emp.deptno = dept.deptno
AND NOT EXISTS
(SELECT *
FROM salgrade
WHERE emp.sal BETWEEN losal AND hisal)

Este comando carregará a tabela PLAN_TABLE com informações da resolução do plano de acesso pelo
otimizador. Para melhor visualizar os dados coletados podemos usar o select abaixo de modo a ter uma
visão do processo executado.

SELECT LPAD(' ',2*(LEVEL-1))||operation||' '||options


||' '||object_name
||' '||DECODE(id, 0, 'Cost = '||position) "Query Plan"
FROM plan_table
START WITH id = 0 AND statement_id = 'Emp_Sal'
CONNECT BY PRIOR id = parent_id AND statement_id ='Emp_Sal'

17
Esta query obterá o seguinte resultado para o comando acima analisado
Query Plan
------------------------------
SELECT STATEMENT Cost = 5
FILTER
NESTED LOOPS
TABLE ACCESS FULL EMP
TABLE ACCESS FULL DEPT
TABLE ACCESS FULL SALGRADE

Interpretando o resultado observamos via nível de identação o processo utilizado pelo server para
resolver a query. No primeiro nível verificamos que foi aplicado um filtro composto de um nested loop
( join de emp com dept) e um full-table scan para a tabela salgrade (not exists). Verificamos também que
no nested loop do join não foi utilizado índice para o acesso a table dept, provavelmente por ser pequena
e um full table scan ser mais eficiente).

Verifique estes conceitos no item 1.2.4 (G).

Para maiores informações sobre o explain plan consulte os manuais:

Oracle Server Application developer’s guide - apendice B.


Oracle Server Concepts Manual - Capitulo 13
Oracle Server Sql Reference.

1.5.2- Tkprof/Sql Trace

Outro modo de se analisar performance é utilizando o trace que o server disponibiliza. Para ativa-lo deve
se executar o comando ALTER SESSION SET SQL_TRACE = TRUE/FALSE. , este comando liga e
desliga a geração de um arquivo de trace de sua sessão no server. Após o arquivo de trace ter sido gerado
usa se o programa TKPROF para formatar o arquivo de trace de modo a que possa ser analisado.

Alem da saída similar ao EXPLAIN o trace informa estatísticas sobre todos os comandos sql encontrados,
tais como:

· parse, execute e fetch counts


· CPU e tempos utilizados, caso o parâmetro TIMED_STATISTICS estiver configurado no arquivo de
inicialização.
· Numero de leituras físicas e lógicas
· Numero de linhas processadas
· Numero de vezes que se tentou ler um bloco do cache e não foi encontrado.

18
Exemplo de parte de uma saída de um arquivo gerado pelo TKPROF.

SELECT * FROM emp, dept WHERE emp.deptno = dept.deptno

call count cpu elapsed disk query current rows


---- ------- ------- --------- -------- -------- ------- ------
Parse 1 0.16 0.29 3 13 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1 0.03 0.26 2 2 4 14

Misses in library cache during parse: 1


Parsing user id: 8

Rows Execution Plan


------- ---------------------------------------------------

14 MERGE JOIN
4 SORT JOIN
4 TABLE ACCESS (FULL) OF 'DEPT'
14 SORT JOIN
14 TABLE ACCESS (FULL) OF 'EMP'

Onde

· Count- É o número de vezes que um statement foi “parsed”,”Fetched” ou “Executed”


· Cpu- É o número gasto da CPU em segundos em cada fase.
· Elapsed- É o número em segundos gasto em cada fase
· Disk- É o numero total de data blocks fisicamente lidos dos data files em cada fase.
· Query- Número total de buffers adquiridos em “consistent mode” para cada fase.
· Current- Número total de buffers adquiridos em “current mode” para cada fase.
· Rows- Número total de linhas processadas pelo comando SQL. Este total não inclui linhas
processadas pelas subqueries.

Observações:

A soma de query+current é o número total de buffers acessados.

Para utilizar este procedimento contate o DBA.

Para maiores informações sobre o processo consulte o manual Oracle Server Application Developer’s
Guide - Apêndice B

19
1.6- DATABASE HINTS

É possível forçar o otimizador em determinadas ações através dos database hints, são comandos
colocados dentro de comentários que determinam caminhos que o otimizador deverá executar, as vezes é
possível que o desenvolvedor conhecendo melhor o seu dado escolha uma opção melhor que o otimizador
escolheria, por exemplo.

Observações:

· Os hints ao invés do comando ALTER SESSION SET OPTIMIZER_GOAL só se aplicam no


comando em que são inseridos, num union ou subquery cada query deve ter seus hints delimitados.
· Os hints são codificados dentro de comentários logo após as palavras chaves DELETE, SELECT ou
UPDATE, qualquer hint em comentários posicionados em outro ponto do comando serão
ignorados.
· Hints não são suportados dentro de PL/SQL 1.0 como dos aplicativos SQL*FORMS, o PL/SQL 1.0
remove qualquer comentário enviado ao servidor com isso o hint não chega ao server.
· Hints conflitantes entre si são ignorados pelo server
· Ao usar hint o otimizador assume uma meta por custo, não é possível utiliza-los com otimização por
regra.
· Ao utilizar views deve se utilizar o hint diretamente no fonte da view.

1.6.1- Hints disponíveis para o desenvolvedor:

Deve se observar que o uso de database hints deve ser acompanhado pelos AD/DBA/Suporte.

A) ALL_ROWS - Tem como meta o melhor resultado para todas as linhas, é o default e é mais indicado
para processamentos batch. Ex.

SELECT /*+ ALL_ROWS */ empno, ename, sal, job


FROM emp
WHERE empno = 7566

B) FIRST_ROWS - A query é resolvida de modo a se obter uma primeira resposta o mais rápido
possível, interessante de ser usada para consultas na tela.

SELECT /*+ FIRST_ROWS */ empno, ename, sal, job


FROM emp
WHERE empno = 7566

Obs: Este hint é ignorado para comandos DELETE e UPDATE e comandos SELECT que contenham
operadores de conjuntos ( UNION, MINUS, INTERSECT, etc..), group by, for update, distinct, funções
de coluna

C) RULE - Forca o otimizador a utilizar otimização por regra para este comando.

SELECT --+ RULE


empno, ename, sal, job
FROM emp WHERE empno = 7566

D) FULL(tabela) - Indica que o otimizador deve utilizar um full-table scan para a tabela especificada

SELECT /*+ FULL(a) Don't use the index on ACCNO */ accno, bal

20
FROM accounts a
WHERE accno = 7086854

Caso a tabela tenha recebido um identificador, este deve ser usado no lugar do nome da mesma.

E) ROWID(table) - Força um table scan por rowid na tabela especificada.

F) HASH(table) - Força um hash scan na tabela especificada

G) CLUSTER(table) - Força um cluster scan por rowid na tabela especificada.

H) INDEX(table [index..indexn]) - Força um index scan pelo índice especificado na tabela especificada.

SELECT /*+ INDEX(patients sex_index) Use SEX_INDEX, since there are few male patients */
name, height, weight
FROM patients WHERE sex = 'M'

Onde patients é uma das tabelas e sex_index um de seus indices.

Caso não seja informado um índice o otimizador avaliara o custo de todos os índices disponíveis, se
informado 1 ou mais a escolha se dará entre os índices informados.

J) INDEX_DESC(table [index..indexn]) - Força um index scan pelo índice especificado na tabela


especificada de forma descendente. Este hint é ignorado quando mais de uma tabela é acessada no select.
SELECT /* INDEX_DESC(tank_readings un_time) */ temperature
FROM tank_readings
WHERE time <= TO_DATE(:t)
AND ROWNUM = 1

K) AND_EQUAL(table index..indexn) - Força um merge dos índices especificados de modo a resolver a


query, mínimo de 2 índices devem ser especificados e máximo de 5.

L) ORDERED - Informa que os joins devem ser executados na ordem da clausula from.

SELECT /*+ ORDERED */ tab1.col1, tab2.col2, tab3.col3


FROM tab1, tab2, tab3
WHERE tab1.col1 = tab2.col1 AND tab2.col1 = tab3.col1

Um join de tab1 com tab2 será efetuado e o resultado será utilizado num join com tab3

M) USE_NL(table) - Força que o join seja um nested-loop com a tabela especificado usada como
inner table.

SELECT /*+ USE_NL(customers) Use Nested loops to get first row faster */
accounts.balance, customers.last_name, customers.first_name
FROM accounts, customers
WHERE accounts.custno = customers.custno

N) USE_MERGE(table) - Forca que a tabela especificada seja “joined” via um sort_merge com o
resultado/ tabela anterior utilizada na ordem do join.

21
1.7- O CONCEITO DE TRANSAÇÃO

Transação é um ou mais conjuntos de ações na base de dados ( inserts, deletes, updates) que constituem
uma unidade lógica mínima que garanta a consistência de seus dados dentro de regras do sistema. Este
conjunto de ações só pode ser aplicado no banco de dados como um todo, caso parte das ações tenham
sido aplicadas e haja uma falha numa ação intermediária, todo as ações já efetuadas neste conjunto
parcialmente executado devem ser desfeitas. Isto é todas as ações são confirmadas ou todo o bloco deve
ser desfeito. Ex.

Processo Admissão funcionário

retirar funcionário da lista de candidatos


incluir funcionário no cadastro de pessoal
incluir dependentes no cadastro de dependentes
retirar requisição de recurso humano do cadastro de necessidades.

Não é possível que o processo se interrompa no meio, caso somente as etapas 1,2 e 3 fossem efetivadas,
continuaria com a requisição de vaga em RH, se somente as etapas 1 e 2 fossem realizadas este
funcionário não receberia seguro família, etc.. etc..

É de responsabilidade do desenvolvedor delimitar as transações, para isto deve ser usado o comando
COMMIT para confirmar a transação ou o comando ROLLBACK para cancelar a transação e reverter as
ações já executadas desde o inicio da transação.

commit /* Fim da transação anterior e começo da transação. A */


delete from lista_candidatos where ...
insert into cad_pessoal ..... A
insert into cad_dependentes ....
delete from cad_necessidades where ...
commit /* Fim da transação A e começo de nova transação. */

O Comando commit termina uma transação e inicia outra, se uma queda do sistema ocorrer ou um
comando rollback for efetivado todos os comandos em aberto ( ate o commit anterior ) serão desfeitos.

1.7.1- Cuidados no uso da transação

· Defina bem o conjunto mínimo de ações de uma transação

Entenda o processo envolvido e regras do seu sistema de modo a determinar logicamente o que é o
conjunto de ações mínimas para se manter os dados íntegros.

· Evite transações muito longas (vários conjuntos lógicos dentro de um mesmo commit)

Enquanto seu dados alterados não forem commitados, a imagem anterior dos mesmos fica
armazenada em áreas de ROLLBACK. Estas áreas são finitas e comuns para todo o banco de dados,
pode não haver área de rollback disponível para uma transação muito longa ou gerar falta de área para
outras aplicações e em caso de falha uma transação muito longa demorará bem mais para desfazer as
ações pendentes. Os dados alterados também ficam em LOCK, isto pode gerar contenção caso outro
usuário necessite altera-los, ao término da transação os locks são liberados.

22
·
· Evite transações muito curtas

No outro extremo evite também commitar a cada linha ou em grupos de ação muito pequenos, cada
commit efetuado gera um overhead, um processamento adicional para liberação de locks e áreas de
rollback. Use o bom senso tendo em vista o tamanho das áreas de rollback disponíveis, número de
usuários concorrendo com os dados e numero de linhas afetadas. Consulte seu DBA para um apoio
nestas decisões.

· Dados não commitados não são vistos por outros usuários (sessões)

Lembre-se que qualquer dado alterado pela sua sessão, só se torna visível a outros após o commit.

1.7.2- Locks

Todo dado alterado numa transação e não commitado fica em estado de lock, outros usuários podem acessar a
informação mas não poderão altera-los até que o commit ou rollback sejam efetuados. Em um ambiente on-line
com vários usuários alterando mesmas informações uma transação que prenda uma linha muito tempo poderá
causar contenção, é importante que se mantenha os lock o mínimo necessário.

Não execute comandos explícitos de lock se consulta ao AD/DBA.

1.7.3- Dead Locks

Procure evitar dead locks atualizando tabelas sempre na mesma ordem,

Sessão 1 Sessão 2
update func set cargo = 27 update depend set ind_saude= ´4´
where cd_mat =13 where cd_mat = 13
update depend set id_vip = update func set vl_sal = vl_sal *
´S´ where cd_mat = 13 1.10 where cd_mat =13

Esperando sessão Esperando sessão


2 liberar 1 liberar

23
2- PL/SQL - CONCEITOS E GUIA DE UTILIZAÇÃO
2.1- O MOTOR DO PL/SQL
PL/SQL é a extensão do Oracle para comandos procedurais, atualmente existem 2 versões de “motores”
disponíveis no ambiente de desenvolvimento Oracle, uma versão 1.1 para tools ( Forms, Report, Graphics) e 2.1
disponível no Oracle Server 7.1. é importante identificar as diferenças de modo a tornar o código de rotinas
portáveis.

Existem as seguintes diferenças entre os ambientes:

2.1.1- Tools PL/SQL (1.0)

· Esta versão roda no cliente


· Só são enviados ao server os comandos SQL
· Blocos PL/SQL Não são cacheados na SQL/Área
· Não permite database hints , o pl/sql retira todos os comentários do sql antes de envia-lo para
server.

2.1.2- Server PL/SQL (2.0)

· Esta versão roda no server e não no cliente.


· É a versão utilizada pelas stored procedures
· Implementa novos tipos de estruturas de dados como pl/sql tables
· Os blocos PL/SQL são parseados e mantidos no cache da SQL/Área

2.1.3- Considerações comuns

As funções básicas dos SQL (Decode, sysdate, substr, add_months, etc..) estão disponíveis no motor PL,
não é necessário o uso do select from dual para poder utilizá-las,, basta utilizá-las diretamente no código.

Substitua
declare
datatmp date;
begin
select sysdate + 5 into datatmp from dual;
end;
por
declare
datatmp date;
begin
datatmp:= sysdate + 5;
end;

Isto evita ida ao banco de dados.

24
2.2- USO DE VARIAVEIS

Pl/sql possui tipos básicos e subtipos mais abrangentes que os tipos permitidos nas tabelas, o tipo number
por exemplo possui os seguintes subtipos

Tipo original NUMBER[(precision, scale)]

Subtipos

DEC
DECIMAL
DOUBLE PRECISION
FLOAT
INTEGER
INT
NUMERIC
REAL
SMALLINT

Estes subtipos na realidade tem a mesma funcionalidade do tipo number, servindo apenas para
compatibilidade e um melhor descrição do tipo de dado que a variável ira suportar.

Aconselha-se a utilizar em variáveis os mesmos tipos dados existentes no database com exceção do tipo
BOOLEAN comum aos dois motores e TABLE (PL 2.0).

2.2.1- Conversão de tipos

A conversão de tipos do PL/SQL pode ser feita explicitamente ou implicitamente, para se converter os
dados explicitamente utilize a seguinte tabela para determinar o modo de conversão:

from / To CHAR DATE NUMBER RAW ROWID


CHAR TO_DATE TO_NUMBER HEXTORAW CHARTOROWID
DATE TO_CHAR
NUMBER TO_CHAR TO_DATE
RAW RAWTOHEX
ROWID ROWIDTOCHAR

· PL Tambem efetua uma conversão implícita de dados sempre que for possível, a seguinte tabela
mostra as conversões possíveis de serem feitas automaticamente.

from / To CHAR DATE LONG NUMBER RAW ROWID VARCHAR2


CHAR SIM SIM SIM SIM SIM SIM
DATE SIM SIM SIM
LONG SIM SIM SIM
NUMBER SIM SIM SIM
RAW SIM SIM SIM
ROWID SIM SIM
VARCHAR2 SIM SIM SIM SIM SIM SIM

25
As conversões só podem ser feitas se o conteúdo dos campos forem compatíveis com o tipo do dado
destino e o formato padrão de conversão. Ex.

declare
a varchar2(20);
b number;
c date;
begin
a:= 123;
b:=’123’;
b:=’12A’; /* ERRO ! */
c:=’10-JAN-95’;
c:=’10/1/95’; /* ERRO ! Não é o formato padrão de data */
end;

Utilize conversão explicita , é mais eficiente para o PL.

2.2.2- %TYPE e %ROWTYPE

Na hora de declarar as variáveis procure sempre utilizar em definições os operadores %TYPE e


%ROWTYPE na definição de variáveis e registros, %type se referencia a uma coluna de uma tabela da
base e %ROWTYPE para se criar um registro com a mesma estrutura de uma tabela ou cursor.

Ex.

DECLARE
vnmo_func func.nm_func%type;
emp_rec emp%ROWTYPE;
CURSOR c1 IS SELECT deptno, dname, loc FROM dept;
dept_rec c1%ROWTYPE;
...

O uso destes atributos garantem uma consistência de seus programas com a base definida, caso haja
alguma alteração em colunas da base, o seu programa compatibilizará as variáveis com a base.

2.3- ESTRUTURAÇÃO DE PROGRAMAS

· Estruture bem seus programas, o PL/SQL fornece mecanismos para isso : Procedures e functions.

· Tenha em mente que unidades menores de códigos que executam funções especificas são mais fáceis
de se utilizar, manutender e de se depurar em caso de erro.

· Não faça grandes blocos PL/SQL, utilize ao invés um grande conjunto de pequenos blocos PL/SQL
que contenham funcionalidade bem definidas e que podem ser testados independentemente caso
necessário.

Problemas de memória podem ocorrer causando desde GPFs em ambiente windows, resultados errôneos e
mau comportamento da ferramenta utilizada. Se seu programa for bem estruturado, as procedures terão
sua funcionalidade bem específicas e atomizadas não necessitando muitas linhas de códigos. Tente
manter uma meta de no máximo 100 linhas por procedure, se sua rotina começar estender muitas linhas
de código tente verificar a possibilidade de encapsular parte de seu código em rotinas menores.

· Procedures e functions podem ser definidas dentro de um programa PL/SQL ou de outra procedure
ou function e ajudam a conservar memória no momento que variáveis definidas internamente a
uma procedure só alocam recursos de memória durante a execução das mesmas.

26
· Deve-se no começo de um programa declarar todas as procedures utilizadas no mesmo ( foward
declarations) , isto evita erro de procedures não declaradas a tempo de compilação, permite uma área
do fonte para com a relação de todas as procedures utilizadas e permite que se insira as rotinas em
ordem alfabética o que facilita a manutenção.

DECLARE
/* Declarações de procedures utilizadas no sistema (Foward) */
PROCEDURE calc_taxa (...); -- Calcula taxa servico
PROCEDURE calc_bonus (...); -- Calcula bonus
/

/* Programas podem ser definidos em ordem alfabetica. */


PROCEDURE calc_bonus (...) IS
BEGIN
calc_taxa(...);
...
END;

PROCEDURE calc_taxa (...) IS


BEGIN
...
END;

2.3.1- OVERLOAD

Procedures e functions podem ser “overloaded’ dentro de um programa pl/sql, isto permite que uma única
função tenha uma única funcionalidade e lógica implementada diferentemente dependendo dos
parâmetros passados. Ex.

declare
procedure msg (a in varchar2,b in number ) is
begin
dbms_output.put_line(a||' - Cod:'||to_char(b)) ;
end;

procedure msg (a in number ) is


begin
dbms_output.put_line(' Cod:'||to_char(a)) ;
end;

procedure msg (a in varchar2) is


begin
dbms_output.put_line(a) ;
end;

procedure msg (a in date) is


begin
dbms_output.put_line(to_char(a,'DD/MM/YY')) ;
end;

begin
msg(12);
msg(sysdate);
msg(' Teste 1 ',12);
msg(' Resultado OK');
end;

Overload só é possível em funções e procedures de um programa, package ou de uma função ou


procedure de um nível maior. Programas, procedures, packages e funções independentes não podem ser
overloaded.
Seu uso é indicado para criação de rotinas genéricas em libraries ou packages. Permite que se
disponibilize funções “únicas” ao usuário que trate de forma diferenciada varias combinações de
parâmetros a fim de se obter uma mesma funcionalidade.

27
O usuário tem aparentemente disponível uma única função que pode ser implementada para todos os
tipos de dados que surgirem a fim de se obter o resultado lógico desejado.

2.3.2- Storage Procedures e functions

As procedures, functions e packages podem ser armazenadas no banco de dados de forma independente.
Este procedimento traz várias vantagens.

· Armazenamento central de código

Permite que regras de negócios comuns a todos os sistemas da empresa sejam armazenadas de forma
centralizada, facilitando seu controle e administração. Disponibiliza seu uso para todas ferramentas que
acessam o banco de dados: Database Triggers, Oracle Tools, Linguagens 3 geração via pre-
compiladores, outras Storages procedures, Banco de dados remotos e ate mesmo outras linguagens via
ODBC como ACCESS.

· Armazenamento de rotinas já compiladas

Processo de parse não é necessário para as storage procedures, elas já são armazenadas compiladas no
banco de dados, em caso de se efetuar alguma alteração no ambiente dos objetos utilizados pela
procedure ( criação ou drop de índices, alteração de tabelas, etc..) ela será compilada durante a próxima
execução.

Obs: É interessante para se evitar possíveis erros que após mudanças na base se recompile as procedures
de modo a evitar que possíveis erros sejam detectados na próxima execução das mesmas.

· Backup de fontes integrado com backup do banco de dados

Todos os fontes das stored procedures estão salvos no dicionário do banco de dados, assim estão
sempre disponíveis para uma recompilação e alteração.

28
· Evitar tráfego na rede

Em processamentos distribuídos ou client/server pode se usar os storage procedures para se evitar o


trafego dos resultados de comando SQL pela rede,

No caso de se enviar a procedure para o server transformando num stored procedure este tráfico
diminuirá sensivelmente.

· Objeto do banco passível de grants

As stored procedures são objeto do banco de dados passíveis de serem tornadas de execução publica
ou para users selecionados, As tabelas acessadas pelas procedures necessitam de grant para o owner
da procedure, o criador da mesma. Um user que tenha grant de execução de determinada procedure
não necessita de ter acesso direto as tabelas envolvidas em seu código, esta característica facilita a
administração e controle de segurança

Obs:

1- As informações disponíveis sobre as stored procedures estão disponíveis nas seguintes tabelas do
catálogo :
ALL_ERRORS, USER_ERRORS, DBA_ERRORS
ALL_SOURCE, USER_SOURCE, DBA_SOURCE
USER_OBJECT_SIZE, DBA_OBJECT_SIZE

2- Consulte seu AD/DBA antes de colocar uma procedure no banco de dados.

29
2.3.3- Estrutura de Packages

Packages são conjuntos de procedures, functions , exceptions e variáveis definidas num único bloco. uma
package é dividida entre 2 áreas, uma área de especificação onde são definidas o que será visível ao
usuário e uma área de implementação (body) onde se definem o corpo das procedures definidas na
especificação e outras procedures auxiliares não disponíveis ao usuário e sim as outras procedures da
package.

Permite tambem que se criem variáveis comuns as procedures da package e disponíveis ao usuário
durante o transcorrer da sessão.

A estrutura de package deve ser usada na criação de bibliotecas unindo rotinas que tratam de assuntos
afins.

2.3.4- Packages Pré-Definidas

Uma série de package estão pre-definidas no database para uso do desenvolvedor, entre elas podemos
destacar

· DBMS_OUTPUT

Esta package é importante na depuração de programas PL/SQL no ambiente SQL/PLUS, podendo emitir
mensagens sinalizando o caminho executado pelo seu programa. Ex.

/
SET SERVEROUTPUT ON
/
BEGIN
..
..
IF ( flag = ‘S’) THEN
INSERT INTO lancamentos ( cd_lanc, dt_lanc , vl_lanc) VALUES ( ‘01’, SYSDATE, vlCalc);
dbms_output.put_line(‘LANCAMENTO INSERIDO ‘);
END;
..
..
END;

Para o resultado aparecer deve-se executar o comando SQLPLUS SET SERVEROUTPUT ON

· DBMS_SESSION

Esta package permite definir alguns controles de sessão como set role e set sql trace, etc..
Ex.: dbms_session.SET_ROLE() e dbms_session.set_sql_trace(true);

· DBMS_DDL

Esta package permite alguns controles sobre stored procedures, entre elas DBMS_DDL.COMPILE_ALL
que compila todas as procedures na base.

· DBMS_LOCK

Permite a criação de locks lógicos, como por exemplo se bloquear um processo quando determinada
impressora estiver em uso, para isso a aplicação cria um lock lógico para a printer de modo que se outra
processo tentar obter o mesmo lock não terra sucesso.

30
· DBMS_ALERT

Permite troca de informação entre sessões de forma síncrona ( dependem de commit)

Exemplo:

App1:

dbms_alert.register('emp_table_alert'); /* Registro um alert */


readagain:
/* ... read the emp table and graph it */
status := dbms_alert.waitone('emp_table_alert', :message); /* Espero o alert */
if status = 0 then goto readagain; else
/* ... error condition */
..

Crio uma trigger na tabela EMP.

CREATE TRIGGER emptrig AFTER INSERT OR UPDATE OR DELETE ON emp


BEGIN
dbms_alert.signal('emp_table_alert', 'message_text'); /* A cada alteração disparo o alert! */
END;

A Aplicação cima é avisada sempre que uma inclusão, alteração ou exclusão for efetuada na tabela EMP.

· DBMS_PIPE

Permite troca de informação entre sessões de forma assíncrona ( não dependem de commit)

Exemplo:

CREATE OR REPLACE PROCEDURE debug(info VARCHAR2) IS


s NUMBER;
BEGIN
DBMS_PIPE.PACK_MESSAGE(info);
s := DBMS_PIPE.SEND_MESSAGE('plsql_debug');
IF s <> 0 THEN
RAISE_APPLICATION_ERROR(-2000, 'Debug error');
ENDIF;
END;

Esta rotina manda uma mensagem de debug para outras aplicações poderem monitorar com o seguinte
segmento de código.

DECLARE
s INTEGER;
chr VARCHAR2(200);
BEGIN
chr := '';
s := DBMS_PIPE.RECEIVE_MESSAGE('plsql_debug'); /* verifico se tem mensagem... */
IF s = 0 THEN /* caso positivo pega a mensagem na variavel chr */
DBMS_PIPE.UNPACK_MESSAGE(chr);
END IF;

31
· DBMS_SQL

Esta package permite que execute comandos sql montados dinamicamente dentro de aplicações.

Exemplos de comando DDL


CREATE PROCEDURE drop_table (table_name IN VARCHAR2) AS
cid INTEGER;
BEGIN
cid := dbms_sql.open_cursor; /* Open new cursor and return cursor ID. */
/* Parse and immediately execute dynamic SQL statement built by
concatenating table name to DROP TABLE command. (Unlike DML
statements, DDL statements are executed at parse time.) */
dbms_sql.parse(cid, 'DROP TABLE ' || table_name, dbms_sql.v7);
dbms_sql.close_cursor(cid); /* Close cursor. */
EXCEPTION
/* If an exception is raised, close cursor before exiting. */
WHEN OTHERS THEN
dbms_sql.close_cursor(cid);
END drop_table;

Exemplo de comando DML .


CREATE OR REPLACE PROCEDURE demo(salary IN NUMBER) AS
cursor INTEGER;
rows_processed INTEGER;
BEGIN
cursor := dbms_sql.open_cursor;
dbms_sql.parse(cursor, 'DELETE FROM emp WHERE sal > :x', dbms_sql.v7);
dbms_sql.bind_variable(cursor, ':x', salary); /* Prepara a variavel */
rows_processed := dbms_sql.execute(cursor);
dbms_sql.close_cursor(cursor);
EXCEPTION
WHEN OTHERS THEN
dbms_sql.close_cursor(cursor);
END

Para maiores informações sobre as packages existentes consulte os manuais

Oracle7 Server Documentation Addendum, Release 7.1 (sql dinâmico)


Oracle7 Server Application Developer's Guide - Apendice A

32
-

2.4- CURSORES
Cursores são estruturas internas utilizadas pelo Oracle para a resolução de comandos SQL, qualquer
comando sql exige um cursor aberto, eles podem ser explícitos ( abertos pelo desenvolvedor ) ou
implícitos ( gerados pelo database para resolver comandos sql ). Segue algumas observações que devem
ser consideradas pelo desenvolvedor:

· Utilize cursor explicito uma vez que é mais eficiente que utilizar cursor implicitamente .

A sintaxe:
declare
vnome func.nm_func%TYPE;
cursor C1 is select nm_func from func where cd_mat = 12;
begin
open c1;
fetch c1 into vnome;
close c1;
end;
é mais eficiente que
declare
vnome func.nm_func%TYPE;
begin
select m_func into vnome from func where cd_mat=12;
end;

No primeiro caso somente um fetch é executado, no caso do comando select into dois fetchs são
executados, um para obter o dado e um segundo para checar a exceção TOO_MANY_ROWS

· O numero de cursores é finito.

Numero de cursores abertos simultaneamente pelo Oracle é finito e definido em parâmetro do init.ora, é
importante que se feche os cursores quando não mais forem utilizados.

· Não utilizar commit em cursor for update.

Não utilize commit durante o processamento de um cursor for update aberto, o mesmo fica invalidado
para alterações, pois ao dar o commit os locks são liberados

· Utilize se necessário cursor utilizando parâmetros

Utilize quando necessário cursor com parâmetros de modo a reutiliza-los já parseados mais de uma vez
na sua aplicação

· Evite abrir cursores dentro de loops de outros cursores

Tente sempre utilizar um join no cursor mais externo, um join normalmente é melhor que “sub selects” dentro
do uso deste cursor.

33
· Não use locks com for update

Caso seja necessário atualizar um linha de um cursor selecione na declaração do cursor a pseudo coluna
ROWID e quando necessário atualizar a linha utilizando este rowid. Ex.

declare

cursor C1 is select cd_mat, dt_adm, rowid from func;


reg C1%ROWTYPE

begin
open c1;
fetch c1 into reg;
..
..
update func set vl_sal = vl_sal * 1.5 where rowid = reg.rowid;

close c1;
end;

· CURSOR FOR LOOP

Procure usar a estrutura de cursor loop, ela apresenta uma sintaxe clara e eficiente na utilização de
cursores.
Exemplo.

DECLARE
salary_total REAL := 0.0;
CURSOR c1 IS SELECT ename, sal, hiredate, deptno FROM emp;
...
BEGIN
FOR emp_rec IN c1 LOOP
...
salary_total := salary_total + emp_rec.sal;

END LOOP;
...
END;

O cursor é aberto, fetched num loop e fechado ao final (%NOTFOUND). A estrutura emp_rec é
automaticamente declarada como %ROWTYPE do cursor.

2.5- RECURSIVIDADE

PL/SQL é uma linguagem com características recursivas, isto é , o PL/SQL permite que uma função
chame ela própria.

· Em muitos casos o uso de recursividade torna o código mais elegante e simples do que uma versão do
mesmo via iteração.

· Deve-se tomar cuidados como a garantia de um termino do processamento de modo a evitar loops.

· Deve-se tomar cuidado com declaração de variáveis que serão alocadas em cada chamada recursiva.
Um numero grande de iterações pode causar erro de pilha de vaiáveis devido a falta de memória.

Exemplo de códigos recursivos vs. iterativos do algoritmo da série de fibonaci:

34
¨ Versão recursiva

FUNCTION fib (n POSITIVE) RETURN INTEGER IS


BEGIN
IF (n = 1) OR (n = 2) THEN
RETURN 1;
ELSE
RETURN fib(n - 1) + fib(n - 2);
END IF;
END fib;

· versão iterativa

FUNCTION fib (n POSITIVE) RETURN INTEGER IS


pos1 INTEGER := 1;
pos2 INTEGER := 0;
cum INTEGER;
BEGIN
IF (n = 1) OR (n = 2) THEN
RETURN 1;
ELSE
cum := pos1 + pos2;
FOR i IN 3..n LOOP
pos2 := pos1;
pos1 := cum;
cum := pos1 + pos2;
END LOOP;
RETURN cum;
END IF;
END fib;

Apesar do fonte na versão recursiva ser mais simples e elegante, ele não é mais eficiente já que a cada
iteração a chamada da função gera overhead e nova alocação de memória na área de stack quando
executado.

Se o numero de iterações for muito grande deve-se optar pelo método iterativo pelo ganho de
processamento, caso este número não seja significativo a aproximação recursiva pode ser utilizada para
uma melhor legibilidade do programa.

35
2.6- PADRÃO DE CODIFICAÇÃO DE PROGRAMAS PL/SQL

É interessante um padrão de codificação de seus programas PL/SQL, isto ajuda na clareza, manutenção e
entendimentos dos mesmo. Sugerimos os seguintes tópicos

2.6.1- Ativação de spool em ambientes SQL*PLUS

Inserir no script do SQL*PLUS o comando

SPOOL &1

De modo a direcionar possíveis saídas de seu programa para um arquivo de log passado como primeiro
parâmetro na linha de comando.

2.6.2- Cabeçalho do programa

Inclua em todos os programas um cabeçalho com nome do programa, sistema, pequena descrição do
objetivo do mesmo, ações nas bases acessadas e lista de versões com data, autor e mudanças efetuadas.

2.6.2- Declaração foward de todas as procedures existentes

Declare o cabeçalho (declaração foward) de todas as procedures e functions utilizadas no programa com
pequena descrição das mesmas.

2.6.3- Funções e procedures envolvidas em ordem alfabéticas

Como todas as rotinas já foram declaradas foward, inclua o fonte das mesmas em ordem alfabéticas
dentro do fonte. Isto facilita a localização das rotinas em caso de manutenção

2.6.4- Corpo do programa e procedures

Utilize as palavras reservadas, procedures e rotinas originais da Oracle em caixa alta (maiúsculas), o resto
do fonte ( variáveis, funções definidas pelo usuários, etc. ) em caixa baixa (minúscula).

Ao terminar uma definição de função ou procedure indique o nome do módulo após o ultimo END do
bloco. (Vide exemplo de fonte)

36
2.6.5- Padrão de nome de variáveis

Utilizar forma geral XXX_YYYYYY_..._YYYYYY onde:

· XXX - Indica três letras identificadoras da natureza da coluna de acordo com o glossário de
termos
· YYYYYY- Até seis letras que identificam cada qualificador da coluna de acordo com glossário
de termos.

Para variáveis de programas adicione os seguintes prefixos:

· WRK_ XXX_YYYYYY_..._YYYYYY - Variáveis de working locais.


· GLOBAL.XXX_YYYYYY_..._YYYYYY - Obrigatório para variáveis globais em
FORMS.
· GLB_XXX_YYYYYY_..._YYYYYY - Variáveis globais em PL/SQL.
· PAR_XXX_YYYYYY_..._YYYYYY - Parâmetros em PL/SQL.

2.6.6- Commit explicito ao termino do programa

Ao término de um programa batch sempre execute o comando commit;

2.6.7- Retorno de SQL.SQLCODE em ambientes SQL*PLUS

Ao termino do bloco principal de seu programa PL/SQL saia sempre do sqlplus com o comando EXIT
SQL.SQLCODE, isto informa ao sistema operacional o sucesso ou falha do processo, isto permite que
scripts tenham um bom controle do processo.

2.6.8- Exemplo de fonte padronizado

SPOOL $1
BEGIN
--
-- Programa FNM003 - Calculo de reajuste de funcionarios.
--
--Este programa varre a tabela de funcionarios atualizando o salario de acordo com índice encontrado na tabela de cargos,
-- caso este índice não seja encontrado o funcionario não tera aumento.
--
--
-- Historico do programa
-- Data Autor Mudancas
-- -----------------------------------------------------------------------------------------------------------
-- 01/04/95 Antonio Jose Entrega de programa para produção
--01/08/95 Antonio Jose Mudanca de acesso para update via ROWID
--01/12/95 Claudia Silva Exception NO_DATA_FOUND para tab_cargo incluido (sem taxa)
--
-- Rotinas utilizadas no programa ( Declaração foward ) -----------------------------------------------------------

FUNCTION calcula_taxa( par_cod_cargo IN NUMBER, -- Calcula a taxa de reajuste para um cargo


par_cod_depto IN NUMBER) -- de determinado departamento
RETURN NUMBER;

PROCEDURE atualiza_sal( par_cod_rowid IN ROWID, -- Atualiza o salario do funcionario apontado


par_val_sal IN NUMBER); -- pelo ROWID.

37
-- Declaração de Cursores e variaveis globais ------------------------------------------------------------------------

CURSOR cur_tab_func IS
SELECT cod_func, val_salario,cod_cargo,cod_depto, ROWID
FROM tab_func;

wrk_val_taxa NUMBER;
wrk_val_sal_novo NUMBER;

--Implementação de procedures e functions ------------------------------------------------------------------------

PROCEDURE atualiza_sal ( par_cod_rowid IN ROWID, par_val_sal IN NUMBER) is


BEGIN
UPDATE tab_func
SET
val_salalrio = par_val_sal
WHERE
ROWID = par_cod_rowid;
EXCEPTION
WHEN OTHERS THEN
RAISE_APLICATION_ERROR(200001,’Erro atualizando salario na tabela func’);
END atualiza_sal;
-------------------------------------------------------------------------------------------------------------------------
FUNCTION calcula_taxa ( par_cod_cargo IN NUMBER, par_cod_depto IN NUMBER)
RETURN NUMBER is

wrk_ind_reajuste NUMBER;

BEGIN
SELECT
val_ind_reaj INTO wrk_ind_reajuste
FROM
tab_cargos
WHERE
cod_cargo = par_cod_cargo AND
cod_depto = par_cod_depto;

RETURN wrk_ind_reajuste;
EXCEPTION
WHEN NO_DATA_FOUND THEN
return 1;
WHEN OTHERS THEN
RAISE_APLICATION_ERROR(200002,’Erro acessando tabela de cargos’);
END calcula_taxa;

--------- Corpo principal do programa -------------------------------------------------------------------------------


BEGIN
FOR wrk_reg IN cur_tab_func LOOP
wrk_val_taxa := calcula_taxa(wrk_reg.cod_cargo, wrk_reg.cod_depto);
wrk_val_sal_novo := wrk_val_taxa * wrk_reg.val_salario;
atualiza_sal ( wrk_reg.ROWID, wrk_val_sal_novo);
END LOOP;
COMMIT;
END;
/
EXIT SQL.SQLCODE
/

38
3-FORMS 4.0 - CONCEITOS E GUIA DE UTILIZAÇÃO
3.1- CARACTERÍSTICAS DA FERRAMENTA
Forms 4.0 é a ferramenta da Oracle para o desenvolvimento de aplicações on-line baseada em arquitetura
cliente-servidor e numa programação orientada a eventos. Deve-se lembrar que parte do processamento
se realiza em sua máquina (cliente) e parte se realiza no servidor onde está localizado o banco de dados
(server), o entendimento deste conceito permite um balanceamento de recursos de cpu obtendo assim
uma melhor utilização dos 2 ambientes.

3.1.1- Divisão do processamento entre ambientes

Roda no Cliente

· Lógica procedural das rotinas locais - Estruturas de desvios, loops e seleção.


· Funções e comandos do PL/SQL (Motor PL/SQL 1.1 local).
· Interface com o usuário, controle de eventos, janelas, objetos gráficos, listas etc.
· Paginação de informações recebidas em blocos multi-registros.
· Paginação de informações via lista de valores (LOV).
· Armazenamento de variáveis.

Roda no Servidor

· Resolução de Queries - ( parse SQL).


· Códigos armazenados em procedures no banco de dados.
· Triggers definidas a nível de banco.
· Implementação de constraints a nível de foreign keys e checks de validação de coluna.
· Processos de atualizações na base da dados ( Insert, Delete e Updates).
· Conexões com objetos de base de dados remotas.
· Verificação de privilégios de usuário e acesso a base de dados.

3.1.2- Oracle.ini

Certos controle do forms são parametrizados através de variáveis de ambiente, no ambiente UNIX estas
variáveis são definidas na profile do usuário e no ambiente WINDOWS são variáveis definidas dentro de
um arquivo chamado ORACLE.INI que se encontra normalmente no seu diretório windows. Este arquivo
contem informações para toda a linha de produto Oracle e entre os diversos parâmetros destacar os
seguintes para o ambiente forms:
Objetos UNIX Windows
Localização de fontes (fmb,fmx,etc) ORACLE_PATH FORMS40_PATH
editor FORMS_EDITOR FORMS40_EDITOR
temp files TMPDIR TMP (SET TMP= no dos)
default font FORM40_DEFAULTFONT (4.0.13)
Diretório de icones TK2_ICON TK20_ICON

39
Exemplos:

FORMS40_PATH=H:\ORAWIN\FORMS45\PLSQLLIB; H:\ORAWIN\FORMS45\SOURCES;

Indica 2 diretórios para buscas de forms e libs.

A localização dos fontes e executáveis pelo forms obedece a seguinte regra de busca. Se nome completo
for especificado este será usado, se não :

1. Procura-se o objeto no diretório corrente (working directory).


2. Pesquisa seqüencial em cada diretório especificado pela variável FORMS40_PATH
3. Pesquisa seqüencial em cada diretório especificado pela variável ORACLE_PATH

No caso de libraries, o arquivo .PLL deverá estar no diretório corrente caso não se especifique o path
completo no nome no momento de se atachar a biblioteca, após atacha-la pode se mover para qualquer
diretório apontado pela variável FORMS40_PATH.

O Arquivo Oracle.ini já deverá estar corretamente criado e configurado em seu ambiente de


desenvolvimento, não o altere sem consulta ao responsável pelo sistema/AD/DBA

3.1.3- Codificação de eventos

Uma linguagem orientada a eventos não é linear, não existe um fluxo seqüencial de comandos definidos
por aplicação, e sim uma série de pequenos códigos associados a cada evento que o usuário ou sistema
possa provocar. Não existe deste modo um “fonte” único de sua aplicação e sim uma série de pequenos
fontes que são disparados numa seqüência comandada pelo usuário e não pelo programa.

Não faça sua lógica assumindo determinadas seqüências de procedimentos que o usuário “normalmente”
seguiria, sua aplicação tem que estar apta a responder os eventos do usuário em qualquer ordem em que
ele possa provocar com o uso do teclado, mouse, menus e eventos disparados pelo sistema operacional
(windows).

3.2- MODULARIZAÇÃO DE FORMS E O CÓDIGO PL/SQL

Deve-se seguir os mesmos conceitos de modularização discutidos no capitulo inicial de PL/SQL, evite
triggers muito grandes e mantenha seus códigos pl/sql organizados em procedures e functions de acordo
com os padrões discutidos anteriormente.

40
Observações:

3.2.1 - Caso mais de uma trigger dispare um mesmo código, empacote este código numa procedure e
informe a chamada da procedure nas 2 triggers que necessitem disparar o código. Exemplo:

When-Validate-Item ( cd_depto) Post-Query ( cd_depto)


BEGIN BEGIN
popula_nmdepto; popula_nmdepto;
END; END;

PROCEDURE popula_nmdepto
BEGIN
SELECT nm_depto INTO :blk1.nm_depto FROM depto
WHERE cd_depto = :blk1.cd_depto;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;

Isto permite um melhor controle do código em caso de manutenção poupando tambem memória na
estação cliente.

3.2.2- Não crie em um único forms várias windows e várias funções distintas. Mantenha a meta de 1
window principal para cada form ( 1 form ó 1 window ó 1 aplicação ó 1 Função básica). Isto permite que
em caso de alteração somente o form afetado tenha que ser recompilado, alem de só carregar na memória
o que for necessário no momento.

3.2.3- Se o fonte de uma procedure ou trigger passar de 100 linhas procure desmembra-la em unidade
menores. Existe um limite de 64K por código no forms e caso um fonte se aproxime deste limite causara
um GPF no ambiente de desenvolvimento windows abortando o programa e perdendo o trabalho digitado.

2.2.4- Não utilize nomes de procedures e objetos do forms com o mesmo nome de objetos do banco de
dados. Isto causa erro na geração do forms.

2.2.5- Dentro do código PL/SQL sempre se referencie a objetos do forms prefixando o nome do bloco,
isto acelera o processo de compilação e evite erros em futuras manutenções caso novos campos sejam
adicionados com mesmo nome em blocos diferentes.

41
2.2.6- Torne suas procedures o mais independentes possível de seu forms, evite acessar diretamente
dados externos (variáveis globais, campos de tela, etc.) sempre que possível utilize os parâmetros
IN e OUT para a interface com o meio externo. Isto torna seu código mais estanque e mais fácil de
depurar.

Exemplo:

Evitar Ideal
BEGIN BEGIN
.. ..
valida_uf; valida_uf(:cd_uf, wrk_ind_find);
IF :global.find = 1 then IF wrk_ind_find = 1 THEN
.. ..
END; END;

PROCEDURE valida_uf AS PROCEDURE valida_uf( puf IN VARCHAR2,


dummy NUMBER; ret OUT VARCHAR2) AS
BEGIN dummy NUMBER;
SELECT 1 INTO dummy BEGIN
FROM tab_uf SELECT 1 INTO dummy
WHERE cd_uf = :blk1.cd_uf; FROM tab_uf
:global.find:=1; WHERE cd_uf = puf;
EXCEPTION ret:=1;
WHEN NO_DATA_FOUND THEN EXCEPTION
:global.find:=0; WHEN NO_DATA_FOUND THEN
END; ret:=0;
END;

3.2.7- Ao utilizar a built-in MESSAGE() do forms para nível de depuração de programa emita sempre o
comando SYNCHRONIZE, isto faz com que a mensagem enviada seja imediatamente mostrada na tela,
evitando que um erro linhas mais a frente desabilite o processamento do comando previamente emitido.
Se possível crie uma procedure MENSAGEM já incorporando o SYNCHRONIZE após o MESSAGE.

3.2.8- Ao utilizar cores no design utilize somente as cores das 2 colunas mais a esquerda da pallete de
cores. Estas cores são comuns tanto para boilerplates como para itens e são as cores mais básicas do
windows. Itens nem sempre tem as cores mais a direita possíveis, muitas vezes o resultado apresentado é
diferente do que aconteceria em um boilerplate e não são disponíveis para ambiente 16 cores.

3.3- LOCALIZAÇÃO DE PROCEDIMENTOS PL/SQL


O Forms permite que se utilize código PL/SQL (procedures e functions) armazenados em 3 formas
distintas, salvos como rotinas definidas no próprio forms, numa biblioteca externa a nível de file system
do sistema operacional e armazenadas no banco de dados na forma de stored procedures.

Cada uma das formas tem suas características próprias que indicam as diferentes situações em que devam
ser escolhidas como opção de armazenamento .

42
3.3.1- Procedures definidas a nível de forms

Recomendado quando:

· Não forem utilizadas por vários forms


· Contenham procedimentos de cálculos e validações que não necessitem muitos comandos SQL e idas
ao banco de dados.
· Necessitem manipular vários objetos de canvas em seu forms.

Observações:

· Método mais rápido que atachar uma biblioteca (vide 3.3.2)


· Objetos do forms/canvas são diretamente acessados pelo código das rotinas.
· Permite utilização no PL/SQL de built-ins especificas do forms.

3.3.2- Definidas a nível de lib externas

Recomendado quando:

· Forem utilizadas por vários forms


· Contenham procedimentos de cálculos e validações que não necessitem muitos comandos SQL e idas
ao banco de dados.
· Procedimentos possam ser utilizados por outra ferramentas Oracle como reports 2.0.
· Procedimentos sofram atualizações constantes independentes das aplicações.

Observações:

· Permite uma centralização de bibliotecas facilitando o gerenciamento das rotinas básicas de uma
instalação.
· Alteração de uma lib não necessita que se regere os forms que a utilizam.
· Lembrar de se rodar o generate da lib sempre que se alterar alguma rotina de uma lib.
· Objetos do forms/canvas não são diretamente acessados pelo código das rotinas, é necessário que se
utilize as built-ins copy() e name_in().
· Permite utilização no PL/SQL de built-ins especificas do forms.
· Não mesclar em uma única LIB rotinas especificas para forms com rotinas especificas para report,
crie se necessário 2 libs separadas
· Não faça uma grande lib com todas as rotinas existentes, divida em libs menores com procedimentos
afins pois uma lib anexada a um forms vai toda para a memória.
· Em desenvolvimento deve-se atachar a library de produção de modo a se evitar conflitos e erros
quando a library de desenvolvimento for atualizada.

43
3.3.3- Nível de base

Recomendado quando:

· Deseja se manter garantias das regras de negócio.


· Forem utilizadas por vários forms.
· Contenham procedimentos que necessitem muitos comandos SQL e idas ao banco de dados.
· Não contenham rotinas especificas de forms (built-ins).
· Haja necessidade de se utilizar database hints em comandos SQL dentro da rotina.
· Procedimento acessando bases remotas, Permite que seja rodado inteiramente num servidor remoto.
· Procedimento seja pesado e haja uma necessidade de que se rode no servidor para ganho de
performance.

Observações:

· Não é permitido o acesso direto a nenhum objeto do forms, só via parâmetros.


· Utiliza o PL/SQL 2.0.
· Não é permitido o uso de commit e rollback dentro do forms.

3.5- USO DE VARIÁVEIS

3.5.1 - Globais

Minimize o seu uso, já que são variáveis que ocupam bastante memória e não são desalocadas entre um
forms e outro. Muito usadas para passagem de parâmetros entre forms podem ter esta funcionalidade
substituídas por parâmetros. Quando utilizadas como globais dentro de um forms pode se usar as
variáveis de canvas null para se obter a mesma funcionalidade. Se houver necessidade apague as
variáveis ao termino do seu forms de forma a economizar memória.

Observações:

· Ao serem criadas tem visibilidade e gastam memória durante toda a sessão


· São compartilhadas pelos módulos de menu e por todos os forms que forem abertos.
· Cada variável global ocupa 255 bytes de memória
· Não é possível determinar o tipo, são sempre varchar2.
· Podem ser inicializadas com a built-in default(GLOBAL, VALOR), esta built-in evita que variáveis
já alocadas em outros forms venham a ser apagadas numa nova inicialização.
· A área de memória de uma variável global pode ser liberada utilizando-se a built-in
ERASE(GLOBAL).

3.5.2 -Canvas Null

São control-itens definidos em blocos de um forms sem uma canvas associada. Podem ser usados com
mais eficiência no lugar de globais para variáveis que necessitem ter sua visibilidade possível por todo
um forms.

44
Deve se tomar cuidado pois o conteúdo das mesmas é apagado quando se limpa o forms.

45
Observações:

· Tem visibilidade em todos os procedimentos enquanto o forms estiver ativo


· Gastam pouca memória e somente enquanto o forms estiver ativo
· São passíveis de serem acessadas via menu e libs via copy e name_in.
· Permitem simular certas funcionalidade de array utilizando-se de blocos multi-records e programação.
· Tem tamanho, tipo de dado e constraints de range e mascara definidas em suas propriedades.
· São apagadas em comandos do tipo CLEAR_BLOCK ou CLEAR_FORM.

3.5.3- Internas de PL

Variáveis declaradas em triggers ou procedures tem seu escopo definido somente para o bloco PL/SQL
onde foram declaradas e seus sub-blocos. Não são compartilhadas entre triggers e rotinas.

3.5.4- Parâmetros

Devem ser usados sempre que possível como meio de se passar dados entre forms, seu uso é mais
eficiente que o de variáveis globais, porem não é possível usá-los para retornar informações de um form.

Para um forms utilizar parâmetros os mesmos devem ter sido criados a nível de design no forms destino e
uma lista de parâmetros compatível deve ser criada via programação no forms origem.

Exemplo:

DECLARE
pl_id PARAMLIST;
pl_name VARCHAR2(10) := 'tempdata';

BEGIN
pl_id := GET_PARAMETER_LIST(pl_name); -- Crio lista de parâmetros
IF ID_NULL(pl_id) THEN
pl_id := CREATE_PARAMETER_LIST(pl_name);
END IF;

ADD_PARAMETER(pl_id,'EMPRESA',TEXT_PARAMETER,:blk1.cd_emp); -- Adiciono 2 parametros a lista,EMPRESA e


ADD_PARAMETER(pl_id, 'DTINI', TEXT_PARAMETER,:blk1.dt_ini); -- DTINI que deverão estar definidos em APPST3.

CALL_FORM(‘APPST3’, HIDE, NO_REPLACE, NO_QUERY_ONLY, pl_id); -- Chamo o forms passando os parametro

DESTROY_PARAMETER_LIST(pl_id); -- IMPORTANTE ! libero a memória alocada

46
Dentro do forms criar lista de parâmetros correspondente dentro da opção FORM-> PARAMETER.

Observação:

Todo forms possui uma lista de parâmetros pré definida que representa seus próprios parâmetros
definidos a nível de design, assim para se repassar seus parâmetros para outra aplicação pode se usar o
seguinte código:

pl_id := GET_PARAMETER_LIST(‘default’); -- Pego parâmetro default


CALL_FORM(‘APPST3’, HIDE, NO_REPLACE, NO_QUERY_ONLY, pl_id); -- Chamo o forms passando os parametro

3.6- OBTENÇÃO DE DATA/HORA

Caso seu forms necessite uma utilização freqüente de DATA e hora evite a utilização de SYSDATE a toda
hora. O uso de sysdate emite uma chamada ao banco de dados, utilize uma única chamada no inicio do
forms e utilize a variável global no local de sysdate dentro de seu código PL/SQL

When-new-form-instance

:global.DATA_atual=TO_CHAR(SYSDATE,’DD-MON-YY’);

E ao invés de utilizar a built-in SYSDATE utilize:

:DT_LANC = TO_DATE(:global.DATA_atual);

Caso seja importante o fator tempo dentro da hora acessada, pode se utilizar a seguinte procedimento:

1) Na trigger When-new-form-instance adicione o seguinte:

:GLOBAL.SERVERDATE := TO_CHAR(SYSDATE,'DD-MON-YYYY HH24:MI:SS'); -- IDA AO BANCO DE DADOS


:GLOBAL.TIMESTAMP := :SYSTEM.CURRENT_DATETIME; -- HORA DE SUA MAQUINA LOCAL
:GLOBAL.THRESHOLD := '59'; -- TEMPO DE REFRESH em MINUTOS

47
2) Crie a seguinte função

FUNCTION MYSYSDATE RETURN DATE IS


curr_clientdate DATE;
last_clientdate DATE;
time_difference NUMBER;
last_servdate DATE;
BEGIN
last_servdate := TO_DATE(:global.serverdate, 'DD-MON-YYYY HH24:MI:SS');
last_clientdate := TO_DATE(:global.timestamp, 'DD-MON-YYYY HH24:MI:SS');
curr_clientdate := TO_DATE (:SYSTEM.CURRENT_DATETIME, 'DD-MON-YYYY HH24:MI:SS');
time_difference := CURR_CLIENTDATE - LAST_CLIENTDATE;
IF time_difference * 1440 > TO_NUMBER(:global.threshold) THEN -- refresh necessário
:global.serverdate := TO_CHAR(SYSDATE,'DD-MM-YYYY HH24:MI:SS'); -- Ida ao banco
:global.timestamp := :SYSTEM.CURRENT_DATETIME;
RETURN TO_DATE(:GLOBAL.SERVERDATE, 'DD-MON-YYYY HH24:MI:SS');
ELSE
RETURN LAST_SERVDATE + TIME_DIFFERENCE;
END IF;
END;

A Função MYSYSDATE pode agora ser utilizada no lugar da SYSDATE evitando tráfego de rede e
requisições ao banco de dados a cada chamada. A variável global :global.threshold é utilizada como
tempo de refresh, sempre que a diferença entre chamadas ultrapassar o seu valor em minutos.

Esta alternativa ao SYSDATE só se compensa quando um número grande de chamadas a DATA e hora for
requisitado dentro de um processamento.

Regra:

Nunca utilize em seu sistema a variável de sistema :SYSTEM.CURRENT_DATETIME, ela retorna a hora
de sua máquina (PC), uma hora não confiavel que o usuário pode ter alterado via painel de controle do
windows ou comando DATE do DOS.
3.7- LISTA DE VALORES (LOV)
· Não utilize LOVs para mais de 3000 linhas de retorno.

Para um LOV ser mostrado na tela, todo o conteúdo do select é armazenado em arquivo
temporário na estação para depois ser apresentado ao usuário. Em consultas muito grandes a
criação de um bloco base-table separado para consulta ( no mesmo ou em outro forms) se
torna mais eficiente no momento que se pode parametrizar um buffer maior de memória e
que o resultado já é apresentado ao usuário após o primeiro bloco de linhas ser carregados do
banco de dados, não precisando que toda a tabela resultado seja transferida para que o
usuário tenha um retorno e permitindo tambem a utilização de modo ENTER-QUERY.

· Long List

Outra alternativa para LOV’s em grande massa de dados é a utilização do parâmetro long
list. Quando este parâmetro esta configurado o filtro básico que todo LOV apresenta é
disponibilizado para o usuário antes da execução do mesmo, deste modo a query enviada
para o server passa a conter uma condição de where de modo a diminuir o numero de linhas
da tabela resultado.

Utilize quando o número de linhas retornado for maior que 200 linhas.

48
· Auto Refresh

Desligue esta opção para LOVs em tabelas não muito dinâmicas. Pode-se popular o LOV
uma única vez de modo que na primeira chamada ao LOV a query é executada e em
chamadas subsequentes os dados já existentes na estação cliente são aproveitados não
executando novas chamadas ao banco de dados.

· Único record group para vários LOVs

É possível que um único record-group alimente vários LOVs, deste modo pode-se otimizar o
uso de memória na estação criando um record-group com todas as colunas utilizadas para
pesquisa em uma aplicação e vários LOVs com mapeamento de colunas diferente em cima
deste record-group.

· Quando utilizar os LOV para validação.

Caso um campo tenha um LOV anexado, utilize o para validação quando o número de linhas
retornada for menor que 200 linhas setando atributo do campo ao invés de se re-executar
uma consulta ao banco de dados numa trigger when-validate-item. No momento que o
domínio do campo já esta disponível na estação dentro do record-group do LOV, não é
necessário uma nova query ao banco de dados.

· Ao usar LOVs para validação a coluna mais a esquerda é utilizada como teste.

3.8- TRIGGERS MAIS IMPORTANTES - OBSERVAÇÕES

3.8.1-Key-<TECLA>

As triggers KEY-<TECLA> são disparadas quando uma tecla física (KEY-F1) ou lógica (KEY-
DELREC) é apertada. Numa redefinição de uma tecla que tenha uma funcionalidade pré-definida deve se
incluir a built-in correspondente para se manter a função básica, já que esta funcionalidade se perde com
a definição da trigger.

Exemplo:

Trigger: Key-down
begin
:global.conta:= TO_NUMBER(:global.conta) + 1; -- ADICIONO FUNCIONALIDADE
down; -- FUNCIONALIDADE DEFAULT
end;

3.8.2- Key-others

Deve-se tomar cuidado com o uso da KEY-OTHERS , esta trigger anula todas as funcionalidades default
das teclas do forms. Qualquer tecla KEY-<TECLA> que não tenha uma trigger definida fica anulada e
causa o disparo da KEY-OTHERS.

49
3.8.3- Do-key

Utilize a trigger DO-KEY para disparar ações correspondentes a teclas ao invés de ativar as built-ins
diretamente. Deste modo, pode se manter o código correspondente às ações das teclas centralizado nas
triggers correspondentes.

Exemplo:

Trigger Key-delrec
BEGIN
:global.conta:= TO_NUMBER(:global.conta) + 1; -- ADICIONO FUNCIONALIDADE
DELETE_RECORD; -- FUNCIONALIDADE DEFAULT
END;

Código PL/SQL
BEGIN
IF :in_status = ‘S’ THEN
DO_KEY(‘DELETE_RECORD’); -- AO INVES DE “DELETE_RECORD”
END IF;
END;

Observação:
A trigger chama a própria built-in caso não haja nenhuma trigger definida para a mesma.

3.8.4 Post-change

Não a utilize. Esta trigger existe para compatibilidade com versões anteriores e será descontinuada em
futuras versões do forms. Em seu lugar utilize as triggers WHEN-VALIDATE-ITEM e POST-QUERY.

3.8.5- When-new-???-instance

Este grupo de triggers são a alternativa para se usar as procedures restritas que são proibidas a nível de
triggers PRE- e POST-. Lembre-se que ela é disparada ao término de um processo de navegação quando
o campo destino recebe o cursor (foco), não sendo disparada a nível de programação (passar pelo campo
via go_item) e que em caso de falha da trigger (RAISE FORM_TRIGGER_FAILURE) o foco não volta
para o campo de origem da navegação.

3.8.6- When-validate-item

Utilize para adicionar validação a um item quando a regra de validação envolvida não depender de outros
campos do mesmo bloco.

A trigger só é disparada quando se “passa” pelo item ou uma validação do bloco é efetuada, não
disparando quando o conteúdo do campo é modificado via programação ou carregado por um LOV
quando o cursor estiver em outro campo.

Para forcar a validação do bloco utilize a built-in ENTER.

50
3.8.7- When-validate-record

Utilize para adicionar validação a um item quando a regra de validação envolvida depende de outros
campos do mesmo bloco.

A trigger só é disparada quando se “sai” do registro corrente. Para forcar a validação do bloco utilize a
built-in ENTER.

3.8.8- Post-query

Utilize para popular campos de controle dentro de um execute query, é importante lembrar que em caso
de erro na trigger a linha da tabela que disparou a trigger não é mostrada na tela.

Exemplo:

Trigger Post-Query a nível de Bloco base table da tabela FUNCIONARIO

BEGIN
SELECT nm_depto into :blk1.nm_depto FROM depto
WHERE cd_depto = :blk1.cd_depto;
END;

Esta trigger deverá traduzir os nomes de departamento dos funcionários num execute_query, porem caso
um funcionário não tenha um código de departamento válido a execução da trigger causará erro o que
filtra-ra o registro da query sem que o usuário seja informado.

3.8.9- Pre-query

Utilize para adicionar condições de filtro ao bloco onde a query será executada. Esta trigger permite a
simulação do comando ENTER_QUERY, permitindo que se mova valores para os campos base table do
bloco de modo a servirem como filtros adicionais para a query a ser executada.

Qualquer outra trigger que mover valores para os campos do bloco causará a mudança de status do bloco
para INSERT ou CHANGED, o que pediria uma confirmação de alteração dos dados para um eventual
COMMIT antes de uma execução de query.

3.8.10- Pre/Post-????? (Transação)

As triggers post/pre-insert, post/pre-delete e post/pre-update não são disparadas quando se insere, exclui
ou altera registros em seu forms, e sim após um comando commit_form onde todas as alterações são
efetivadas de uma só vez.

Para cada registro de seu forms cujo status esteja como CHANGED será disparada o conjunto de triggers
da ação correspondentes, assim se 4 registros forem alterados será disparada 4 vezes as triggers pre/post-
update.

51
3.9 - INTEGRAÇÃO COM BASE DE DADOS
O Oracle forms permite uma integração com o banco de dados através dos blocos base-table, estes blocos
criam links com tabelas do database facilitando o processo de atualização e consulta a base de dados.

Todo o controle de geração de SQL tanto para recuperar os dados do servidor ou para a geração das
alterações efetuadas pelo usuário ficam a cargo do forms, baseados nas propriedades dos itens do bloco e
nas propriedades “where” e “order by” do próprio bloco.

3.9.1- Fetch, Buffer e Displayed

A propriedades FETCHED, BUFFERED e DISPLAYED controlam o comportamento e o modo com que


o forms ira tratar a transferencia de linhas do servidor para a estação e seu uso durante o browse das
linhas pelo usuário na aplicação. Após um SQL ter sido gerado pelo forms, o servidor resolve a query
gerando uma tabela resultado com N registros e transmite um bloco de registros ( numero de linhas igual
a FETCHED) para a estação do cliente. A aplicação mostrara na tela um numero de registros definido
pelo parâmetro DISPLAYED, ficando o resto bufferizado numa área de memória que comporta um
numero de registros determinado pelo parâmetro BUFFERED.

A medida que o usuário paginar para novos registros que ainda não tiverem sido carregados do server o
forms ira transferi-los do server sempre em numero igual a FETCHED de modo a minimizar o trafego de
rede aproveitando dos recursos de array processing do Oracle e caso o número de linhas da tabela
resultado for maior que o parâmetro especificado em BUFFERED, o Oracle ira armazena-los em
arquivos temporários no file system do cliente.

Deste modo, é preciso tomar cuidado com espaço em disco já que se um bloco base-table executar um
execute_query sem restrições e o usuário emitir um last_record, toda a tabela será transferida para a
estação.
Evite tambem a built-in LAST_RECORD ela forçara todos os fetchs numa única vez o que causara uma
espera maior para o comando ser executado.

3.9.2- Base Table vs. Processamento manual

· Não utilize a técnica de se trabalhar com bloco não base-table e se carregar as linhas das tabelas
utilizando-se de PL/SQL via SELECT INTO e built-in DOWN num loop de modo a popular os
registros e efetuar depois as alterações na base de dados manualmente via programação.

52
Não há justificativa para tal procedimento, o mesmo não deve ser utilizado já que:

à Os selects não iriam aproveitar a característica de array processing do oracle (propriedade FETCHED)
tornando a busca dos dados extremamente mais lenta.
à Não haveria controle eficiente de locks quando uma linha fosse alterada pelo usuário
à Uma codificação muito mais complexa teria que ser produzida de modo a gerar todos os controles de
alteração já disponibilizados pela ferramenta.
à Toda a tabela teria que ser carregada na estação antes que a tela fosse liberada ao usuário

Deve se escolher uma tabela drive que se tornará a tabela ligada ao bloco. Os campos de outras tabelas
podem ser populados na trigger POST-QUERY sendo que alterações em outras tabelas que não sejam a
tabela drive podem ser codificadas nas triggers de transação PRE- e POST- ( Delete, Insert, Update).

· Nunca popule uma tabela base table de outra forma que não seja via built-in EXECUTE_QUERY.
Uma linha populada via SELECT INTO num bloco base-table ira gerar uma inserção quando um
COMMIT_FORM for executado.

· Não processe manualmente nenhum comando de DML na tabela driver dentro de um bloco base-table
(INSERT, DELETE ou UPDATE). O forms ira efetuar toda as inserções, alterações e deleções
necessárias quando a built-in COMMIT_FORM for acionada.

3.9.3- Relação Master-Detail

Em uma relação pode-se minimizar o tráfego na rede e chamadas ao banco de dados configurando de
forma adequado os parâmetros DEFERRED e AUTO-QUERY .

Utilize sempre que possível as opções DEFERRED=TRUE e AUTO-QUERY=TRUE de modo que um


EXECUTE_QUERY só ocorra no bloco filho quando o cursor for posicionado sobre ele.

Isto evita que ao paginar no bloco pai de modo a localizar um registro, chamadas sejam feitas ao banco
de dados executando-se queries para popular o bloco detalhe em registros que não são alvos da pesquisa
desejada pelo usuário.

3.9.4- Modo ENTER QUERY

O modo ENTER-QUERY permite que o usuário faça consultas por qualquer campo do forms. Consultas
em bases muito grande podem se tornar demoradas caso o usuário selecione para consulta campos onde
não existam índices nas colunas especificadas.

Bloqueie as consultas em campos não definidos junto ao usuário como passíveis de consultas via
propriedade QUERY ALLOWED.

53
3.9.5- Blocos base-table baseado em view’s

Quando a definição da tabela drive num bloco base table não for muito clara gerando a necessidade de se
popular várias colunas via PL/SQL pode se optar pela criação de uma view no banco de dados e
informar esta view como base-table do bloco. Este procedimento evita os “sub-selects” via PL/SQL,
trocando-os por um join que é totalmente processado no servidor e aproveitando a facilidade de array
processing que o forms proporciona.

Uma view com join se torna read-only, deste modo as transações não podem ser efetivadas
automaticamente pelo form. Deve-se criar as triggers ON-INSERT, ON-DELETE e ON-UPDATE que
anulam o processamento default do forms evitando tentativas de alteração na view read-only.

O procedimento de alteração nas tabelas que compõe a view deve ser codificada em código PL/SQL
dentro destas triggers. Deve se usar a trigger ON-LOCK para, tambem manualmente, bloquear os
registros nas tabelas dependentes da view.

3.9.6- Ativando o SQL TRACE

Para se analisar o comportamento do forms utilizando a ferramenta TKPROF discutida anteriormente


deve-se inicializar a sessão do aplicativo habilitando o trace.

Para isso utilize o parametro STATISTICS=true na linha de comando ao invocar o forms. Este parametro
tem a mesma funcionalidade de se emitir o comando ALTER SESSION SET SQL_TRACE = TRUE

3.10- INTEGRAÇÃO COM OUTROS FORMS


No ambiente <cliente> utilize a rotina exec_form_menu para chamar um forms de um menu ou outro
forms, esta rotina implementa controles de segurança chamando outros forms via built-in básica
CALL_FORM.

Existem 3 formas básicas de se chamar um form de outro.

· CALL_FORM

O primeiro form fica na memória, enquanto o segundo form fica ativo e ao termino da execução o
controle volta ao form de origem. É a maneira mais comum de se utilizar o encadeamento de forms já
que normalmente este retorno é desejado

· NEW_FORM

O primeiro form sai do ar liberando a memória utilizada e não há uma volta automática ao form de
origem quando um EXIT_FORM é emitido no form filho. Deve ser utilizada quando não há necessidade
de se retornar ao form de origem automaticamente já que seu gasto de memória é bem menor.

· RUN_PRODUCT

Pode ser tambem utilizada para chamar outro forms, seu uso é indicado quando se deseja abrir uma nova
seção no oracle de modo a se ter transações independentes.

54
Observações

· Variáveis globais não são compartilhadas entre forms chamados por RUN_PRODUCT já que eles
trabalham em sessões independentes.

· Ao se chamar NEW_FORM ou CALL_FORM as transações continuam em aberto, um COMMIT no


segundo forms ira confirmar tanto as alterações do primeiro como do segundo form dependendo
de como tiverem sido executadas:

A) Se as alterações tiverem sido feitas via blocos base-table e já tiverem sido enviadas ao banco
de dados via comando POST as mesmas serão confirmadas.

B) Se as alterações tiverem sido executadas via comandos de DML dentro de PL/SQL não há a
necessidade de POST logo serão executadas tambem.

2.11 -LOCK’S NO FORMS


Quando o usuário altera um registro na tela em um bloco base-table o forms emite um lock exclusivo na
linha da tabela correspondente, porem não envia ao banco as alterações evitando alocação de áreas de
rollback e mantendo o controle da alteração no cliente em memória. As alterações só são enviadas ao
forms durante o processo de COMMIT_FORM ou utilizando-se da built-in POST que envia as alterações
ao server porem não terminando a transação, esta característica implica que certos cuidados devem ser
tomados.

2.12.1- Não execute comandos de DDL via built-in FORMS_DDL ou sql dinâmico quando existirem
transações pendentes no seu forms. Qualquer comando de ddl emitido implica num commit o que
liberaria os locks no servidor antes das alterações terem sido enviadas ao forms.

2.12.2- Antes de se chamar um novo forms emita um comando POST caso possa haver transações
pendentes no form de origem. Caso um form filho tente executar um commit_form e este tenha
sido chamado de um form com transações não enviadas ao server ( “posted”) um erro ira ocorrer.

2.12.3- Uma alteração em um control item (não base-table) de um bloco base-table não implica num lock
na linha da tabela de origem. Para que estes itens bloqueiem o registro é necessário que se marque a
propriedade do control item chamada LOCK RECORD.

3.12- FORMS EM MODO CHARACTER


Para se rodar forms em terminais “burros”, terminais unix orientados a caracter deve-se lembrar de certas
características e se tomar algumas providencias :

· Não é possível o uso do mouse.


· Não utilize itens gráficos como listas, botões, check boxes, radio groups, scroll bars, etc..
· Não utilize cores
· Altere a preference PAGCHT para 19 ( Oracle Case)
· Altere a preference PAGCWD para 78 ( Oracle Case)
· Altere a preference WINFHT para 19 ( Oracle Case)
· Altere a preference WINFXP para 1 ( Oracle Case)
· Altere a preference WINFYP para 2 ( Oracle Case)
· Altere a preference WINFWD para 78 ( Oracle Case)
· Altere a preference STMMMB m:\produção\di\geral\v00\tempmnuc ( Oracle Case)
· Altere a preference HLPFRM para m:\produção\di\geral\v00\ofg4schc ( Oracle Case)
· Altere a preference STFFMB para m:\produção\di\geral\v00\ofg4sctc.fmb (Oracle Case)

55
3.13 - BUGS COMUNS NO AMBIENTE FORMS.

A ferramenta FORMS esta em constante aprimoramento e acertos via várias versões


que são freqüentemente disponibilizadas.

Estes bugs normalmente são detectados pelo suporte Oracle e catalogados de acordo
com a ferramenta e o ambiente em que foi detectado. A área de suporte recebe da
oracle um CD-ROM chamado “Oracle Suport Notes” em que estes bugs são
catalogados e seu status atual informado.

Listamos aqui alguns bugs mais comuns encontrados no ambiente FORMS:

3.13.1- Campo com VARCHAR2 muito grandes

A utilização de campos varchar2 com mais de 1600 caracteres pode acarretar GPFs dentro de INSERTs e
UPDATEs dentro de PL/SQL. Se você levar GPF numa procedure e ao isolar o erro detectar que um
comando INSERT ou UPDATE é o causador, verifique a existência de campos VARCHAR2 muito
grandes.

Para resolver troque este campo por 2 ou mais campos varchar2 menores e concatene na hora da inserção
ou update.

Exemplo:
:blkaux.obs1:=SUBSTR(:blk1.observação,1,1000); -- Divido o campo em campos menores
:blkaux.obs2:=SUBSTR(:blk1.observação,1001,1000); --

UPDATE TABELA
SET txt_obs = :blkaux.obs1||:blkaux.obs2 -- Substituo a linha que dava GPF
WHERE ID=27

3.13.2- Library quando aberta em modo read-only

Caso uma library ao ser aberta esteja em modo read-only, verifique se seu working directory está
apontado para um diretório de rede, isto causa a abertura da biblioteca somente para leitura. Indique
como diretório de trabalho seu disco local que o problema será resolvido.

3.13.3- GPF alterando radio groups


Uma alteração de radio group pode gerar GPF se for seguido os seguintes passos.

1. Abrir a folha de propriedades de um radio group.


2. Informar um valor para o campo “Value”. ( Não setar antes qual radio.)
3. teclar OK.

Tome cuidado para sempre escolher qual dos rádios será alterado para não perder o conteúdo de seu
forms por causa do GPF.

56
3.13.4- Sinônimos de database link

Ao utilizar DATABASES LINK dentro de PL de seu forms não utilize sinônimos para eles. O PL/SQL do
forms não reconhece o sinônimo e dará erro informando que a tabela não existe. Utilize explicitamente o
database link quando for necessário acessar tabelas remotas.

3.13.5- RAISE_APLICATION_ERROR em STORED PROCEDURES.

Ao utilizar a built_in raise_aplication_error dentro de uma stored procedure não é possível capturar
dentro do forms o código e a mensagem do erro informado pela stored procedure via
DBMS_ERROR_CODE e DBMS_ERROR_TEXT.

3.13.6- LOVs sem espaço em disco

Quando o espaço em disco não é suficiente, um lov pode não trazer todas as linhas da tabela original. O
oracle forms mostra somente o que pode ser carregado não informando que a lista esta incompleta.

3.13.7- ROS-201 Salvando FORMS em disco

Este erro é causado por um duplicidade interna de itens ID gerados devido a algum problema na hora de
se salvar o forms. É possível recuperar o forms salvando-o como texto (FMT) e procurando o item com
duplicidade para uma alteração manual.

Comunique ao suporte de modo a realizar este processo.

3.13.8- Parâmetros via run_product em maiúsculas

Ao passar parâmetros para um report via built-in run_product os mesmos são convertidos para
maiúsculas

3.13.9- Refresh de objetos no forms designer

Se um objeto desaparecer de sua canvas dentro do design tente fechar seu layout editor e abri-lo
novamente pois em certas ocasiões podem ocorrer problemas de refresh de tela.

Caso o problema persista, verifique as propriedades do item e configure as coordenadas e tamanho para
uma posição visível em seu canvas.

57
4-REPORT 2.0 - CONCEITOS E GUIA DE UTILIZAÇÃO

4.1- DATA MODEL E QUERIES

4.1.1- Defina o mínimo de queries possíveis

Existem 2 caminhos que podem ser utilizados quando se trabalha no report em relatórios tipo master
detail ou que necessitem mais de um grupo de informações correlacionadas.

1- Uma única query onde as tabelas são “joined” de modo a se ter todos os dados existentes e desta query
se criar mais de 1 grupo.

Onde Q_3 é

SELECT dept.deptno, dept.dname, emp.empno,emp.ename


FROM
DEPT, EMP
WHERE
dept.deptno = emp.empno (+)

2- Varias queries simples com um único grupo ligadas por links no datamodel.

58
Onde:

Q_1 é
SELECT deptno, dname
FROM DEPT

e Q_2 é

SELECT deptno, empno, ename


FROM EMP

Ambas as opções geram o mesmo número de grupos com as mesmas informações necessárias para a
montagem do relatório. Apesar da segunda opção normalmente facilitar o entendimento, é recomendado
que se diminua ao máximo o número de queries do report de modo a melhorar a performance.

Levando em consideração as possibilidades existentes siga as seguintes diretrizes:

· Opte por 1 query com vários grupos na grande maioria dos casos. É mais eficiente e gasta menos
recursos do Oracle no momento em que abre somente um cursor.

· Em caso de se desejar manter a funcionalidade que 2 queries proporcionam (todos elementos da


tabela 1 saírem independentes da existência na segunda tabela) faça um outer-join como
demonstrado no exemplo

· Utilize 2 ou mais queries somente quando a tabela pai retornar muito poucas linhas ( < 5) e o
conteúdo de suas colunas selecionadas ocuparem um grande espaço. Isto aumentaria o custo de se
replicar estas colunas por toda a tabela resultado gerada pelo join.

4.1.2- Utilização de parâmetros léxicos

O Oracle Reports permite que utilize os chamados parâmetros léxicos de modo a se definir as queries a
serem utilizadas a tempo de runtime de acordo com parâmetros especificados pelo usuário. Esta
facilidade permite que se construa queries mais simples e com menos objetos ao invés de se ter que
selecionar todas as colunas possíveis fitrando-as no LAYOUT e tambem evita cláusulas where complexas
de modo a atender os parâmetros selecionados.

59
Exemplos do uso de parâmetros léxicos

p_cond := ‘WHERE vl_sal < 2000’


p_col:= ‘CD_CARGO’
ou
p_cond := ‘WHERE to_char(dt_nasc,’’ MM’’) = TO_CHAR(SYSDATE,’’MM’’)’
p_col:= ‘TO_CHAR(DT_NASC,’’DD/MM/YY’’)’

Report (Query):

SELECT cd_mat, nm_func, &p_col info


FROM func
&p_cond

Como vemos no exemplo o mesmo report (query) pode ser utilizado tanto para uma listagem de
funcionários que ganhem menos que 2000 como para listar os aniversariantes do mês.

Regras para o uso de referência léxica

· Incluir um valor default nos parâmetros léxicos de modo a se criar os grupos no design.

· Apelidar toda a coluna que for substituída por parametro léxico.

· Manter a nível de runtime o mesmo tipo de dado para as colunas substituídas.

· Não usar colunas substituídas por parâmetros léxicos em links com outras queries.

· Parâmetros léxicos tem que estar preparados antes da fase do parse, ou seja até a trigger
AFTER_FORM inclusive.

· Podem ser usados em qualquer parte do select inclusive nas clausulas from, order by, group by.

· Um parametro léxico pode conter mais de uma coluna. Deve se manter em tempo de execução o
mesmo número de colunas criados a nível de design.

4.1.3- Utilização de record-group

Pode se passar de dentro de um forms um record-group carregado de modo a substituir uma query de um
report. Para isso deve se incluir na lista de parâmetros passado ao report um parametro do tipo
DATA_PARAMETER, cujo nome deva ser o nome da query que ele ira substituir no report.

Exemplo:

pl_id := CREATE_PARAMETER_LIST('tmpdata');
/*
Adiciono um DATA PARAMETER que substituira a query EMP_QUERY pelo record group EMP_RECS
*/
ADD_PARAMETER(pl_id,'EMP_QUERY',DATA_PARAMETER,'EMP_RECS');

/*
** Inicio o relatório
*/
RUN_PRODUCT ( REPORTS, 'empreport' , SYNCHRONOUS, RUNTIME,
FILEYSTEM, pl_id , NULL);

60
É possível que se crie um relatório somente baseado em record groups gerados pelo forms, neste caso se
não existirem comandos SQL dentro do PL/SQL não haverá a necessidade do report se conectar ao banco
de dados. Para evitar o logon ganhando em performance deve se incluir na lista de parâmetros passado ao
report o parâmetro “logon” com o valor “no”.

Exemplo:
ADD_PARAMETER(pl_id,'LOGON',TEXT_PARAMETER,'NO’);

O record group criado deve manter a mesma estrutura da query a ser substituída. Deve se manter o
mesmo número de colunas e tipo de dados.

4.2- PROCESSAMENTO DE EVENTOS DENTRO DO REPORT


1. BEFORE-FORM é disparado
2. Tela de parâmetros é mostrada
3. AFTER-FORM é disparado
4. Report é compilado
5. BEFORE-REPORT é DISPARADO e queries são parsed
6. SET TRANSACTION READ ONLY é executado caso o parametro READONLY seja
especificado
7. O report é executado
8. BETWEEN-PAGE dispara entre cada página formatada.
9. COMMIT é executado no caso do parâmetro READONLY tenha sido usado
10. AFTER-REPORT é disparada
11. COMMIT/ROLLBACK/NOACTION é executado dependendo do parametro ONSUCCESS

61
Observações

· Utilizar na trigger before-form a built-in DBMS_SESSION.SET_ROLE(‘ALL’);

· Qualquer comando de DDL que altere algumas das queries do report tem que ser executado
antes do passo 4 nas triggers BEFORE-FORM e AFTER-FORM , nestas triggers o
relatório ainda não foi compilado e as queries não foram “parsed”.

· Idem para qualquer construção ou alteração de parâmetros léxicos que serão usados para as
montagens das queries do DATA MODEL.

· Caso o relatório esteja sendo executado em modo readonly não execute comandos que
causem commits como DDLs dentro das triggers BETWEEN-PAGE, pois um commit
acaba com a transação readonly.

· A trigger BETWEEN-PAGE é executada entre cada página FORMATADA pelo report. Não
há garantias da ordem em que as queries são executadas e os dados “fetched”, pois o
processo de execução dos sql não é síncrono com a formatação.

· Utilize a trigger AFTER-FORM para validar os parâmetros passados. Em caso de se retornar


FALSE a tela de parâmetros é reapresentada ou no caso de ter sido desabilitada o controle
volta a aplicação de origem.

4.3- CONSIDERAÇÕES GERAIS NA CRIAÇÃO DE UM REPORT.

Segue algumas regras que devem ser observadas e tidas como metas na criação de um report.

1.3.1 Utilizar o máximo de processamento e transformação de colunas dentro das queries


evitando colunas de cálculos e fórmulas, para cálculos de colunas derivadas dentro
de grupos.

1.3.1 Evite truncar campos no LAYOUT. Utilize SUBSTR nos select das queries quando
necessário.

1.3.1 Só selecione campos que serão utilizados no LAYOUT ou para cálculos de fórmulas.
Verifique ao término de um report se existem colunas em grupos do data model
que não sejam utilizados pelo LAYOUT. Evite queries com “SELECT * “

1.3.1 Evite a utilização de filtros dentro dos grupos. Utilize a condição de where das queries
para isso sempre que possível.

1.3.1 Um campo de fórmula só enxerga colunas de seu grupo ou de grupos acima. Não utilize
em fórmulas campos de grupos filhos ou de queries não correlacionadas

1.3.1 No caso de se limitar o número de registros de uma query utilize a propriedade


MAXIMUM ROWS da query, e não o filtro FIRST de grupos.

1.3.1 Informe um apelido para as colunas que existirem em mais de uma query dentro de seu
DATA MODEL. Isto evitará que o report crie nomes para estas colunas nos grupos
e que em futuras alterações desta query este nome se modifique, criando
incompatibilidade com o LAYOUT definido.

62
1.3.1 Caso uma query de seu DATA MODEL só venha a ser utilizada em determinadas
situações, defina a propriedade MAXIMUM ROWS para 0 (ZERO) dentro da
trigger AFTER-FORM de modo a só processar a query desejada.

Exemplo:
IF :p_flag != 1 THEN
SRW.SET_MAXROW ('Q_STOCKS', 0);
ELSE
SRW.SET_MAXROW ('Q_BONDS', 0);
END IF;

1.3.1 Uma única estrutura do DATA MODEL pode alimentar mais de um único LAYOUT, isto
é 2 ou mais repeating frames podem ser alimentadas por um único grupo do
DATA MODEL. Caso seu relatório exiba layouts alternativos, crie uma única
estrutura no DATA MODEL e crie 2 repeating frames com os layouts desejados,
controlando a escolha via triggers de formatação em cima das repeating frames.

1.3.1 Não utilize frames e objetos sólidos da cor branca. Utilize objetos transparentes no lugar.
Apesar da impressão sair em branco, existe o trabalho na formatação de
“preencher” estes espaços o que causa uma queda na performance e maior
consumo de memória.

1.3.1 Retire frames desnecessárias geradas pelo default LAYOUT. A opção default LAYOUT
gera fixed frames envolta da maioria das áreas de objetos (headers, repeating
frames, etc..) e que muitas vezes não são relevantes para a formatação.

Entendendo a estrutura necessária para seu report retire as frames desnecessárias já que
qualquer objeto a mais sobrecarrega o processo de formatação da página.

1.3.1 Evite utilizar a opção total de páginas em seu relatório ( Ex: Pagina 1 de 30 ). Para o
report calcular o número total de páginas ele formata todo o relatório primeiro
salvando todo o conteúdo em disco para depois imprimir. Esta opção degrada o
processo alem de requerer área temporária na estação.

1.3.1 No caso de se utilizar uma imagem no report, utilize a opção “Reduce Image resolution” .
Esta opção diminui a resolução de uma imagem quando a mesma tem sua escala
reduzida para se adequar ao layout do relatório.

1.3.1 Evite o uso da built-in SRW.DO_SQL dentro de PL/SQL. Ela é menos eficiente que o uso
direto dos comandos dentro do PL e requer um PARSE a cada chamada em tempo
de execução. Use somente quando o objeto acessado não existir em tempo de
runtime.

1.3.1 Não utilize tabelas temporárias criadas dentro do report. Caso seja necessário crie a tabela
com parâmetros ótimos de storage uma só vez e no relatório emita um
TRUNCATE TABLE ao inicio do mesmo. Caso o número de linhas desta tabela
forem significativas utilize a package do banco
DBMS_DDL.ANALYZE_OBJECT(‘TABLE’, owner, tablename , ‘ESTIMATED’)
de modo a colher estatísticas para o otimizador por custo.

1.3.1 O Default LAYOUT apaga um LAYOUT já alterado pelo desenvolvedor. Uma vez que
seu LAYOUT tenha sido alterado manualmente qualquer alteração no DATA
MODEL deve ser codificada tambem manualmente dentro do editor de LAYOUT.

1.3.1 A opção de default LAYOUT “Use current layout settings” não funciona, portanto não
deve ser utilizada.

63
4.4- MONITORANDO PERFORMANCE

4.4.1- SQL TRACE

Para se ativar o SQL TRACE de modo a utilizar o TKPROF insira um campo fórmula a nível de
report cujo valor seja :

srw.do_sql(‘ALTER SESSION SET SQL_TRACE=TRUE’);

4.4.2- PROFILE

O oracle reports pode gerar um arquivo com estatísticas sobre sua exceção no ambiente do cliente. Para
isso deve se ativar a opção PROFILE no menu do report design (RUNTIME SETTINGS) ou como
parâmetro para o run report (PROFILE=nomearq.txt).

Ao término da execução o arquivo será criado com o seguinte LAYOUT:


+-------------------------------------------+
| Oracle Reports Profiler statistics |
+-------------------------------------------+

TOTAL ELAPSED Time: 22.00 seconds

Oracle Reports Time: 20.00 seconds (90.90% of TOTAL)

ORACLE Time: 2.00 seconds ( 9.09% of TOTAL)

UPI: 2.00 seconds


SQL: 0.00 seconds

TOTAL CPU Time used by process: N/A

Onde:

· Total Elapsed Time é o tempo total gasto entre a execução do R20RUN e o término do relatório
· Oracle Reports Time é o tempo gasto na ferramenta cliente
· Oracle Time é o tempo gasto no servidor, onde

· UPI é o tempo gasto com conexões, parse e fetch dos dados


· SQL é o tempo gasto com comandos SRW.DO_SQL

· Total CPU é o tempo gasto com CPU para todo o processo . Disponível quando a opção de estatísticas
de tempo estiver habilitada no servidor.

64
4.5- REPORT CHARACTER MODE

A geração de um report em character mode na versão 2.0 do report deve ser utilizada para uso dentro do
ambiente do servidor (Unix). No ambiente windows não se pode usar o report para geração de relatórios
ou arquivos seqüenciais ASCII (char mode). No ambiente windows não é possível a geração em disco de
um arquivo seqüencial com a saída do mesmo.

Um relatório em character mode pode porem ser desenvolvido e testado no ambiente windows e
transferido o RDF via ftp para o ambiente unix de modo a roda-lo no servidor. Deve se observar os
seguintes pontos:

· Configurar no report as propriedades character mode dentro de global properties.

· Não utilize objetos gráficos como linhas, boxes, imagens, cores e shades.

· Utilize uma fonte de tamanho fixo ( Exemplo: Courrier New)

· Defina as seguintes preferences do CASE para obtenção dos templates necessários:

LAYREP= m:\producao\di\geral\v00\cgchptsc.rdf (80 colunas)


LAYREP= m:\producao\di\geral\v00\cgchissc.rdf (132 colunas)

Grande parte do tempo que um report leva é devido ao processo de formatação de suas páginas no
ambiente gráfico. Um relatório é candidato a ser rodado em character mode quando não há a necessidade
de um acabamento mais requintado em sua apresentação. Relatórios para conferencia de dados, relatórios
legais e listagens muito grandes ( estoques, lançamentos contábeis, etc..) devem de acordo com a
disponibilidade do server serem rodados em character mode.

Outro uso é quando se tem a necessidade de gerar arquivos em meios magnéticos para interfaces com
outros sistemas.

4.6- CLIENT OU SERVER ?

Rodar um relatório no servidor é indicado quando o processamento de um report é muito pesado para ser
realizado em uma estação. O processo de formatação e impressão de relatórios pode ser demorado, o que
prenderia a estação durante o processamento.

No servidor pode-se rodar tanto relatórios character mode como gráficos ( ambiente motif). A decisão de
se transferir o processamento para o servidor deve seguir as seguintes premissas:

· Necessidade de rodar relatório em character mode.


· Impressões muito grandes ( mais de 70 páginas ).
· Necessidade de se schedular um relatório para fora das horas de pico de uso do banco de dados.
· Necessidade de liberação imediata da estação cliente.

65
4.7- PRINCIPAIS BUG’S NO AMBIENTE REPORT.
Alguns dos bugs mais comuns dentro do Oracle report:

4.7.1- GPF causados por falta de espaço em disco.

Uma grande causa de GPFs no ambiente reports é a falta de área em disco para a geração de arquivos
temporários. Certifique-se que existe uma variável de ambiente TMP=C:\TMP ou para qualquer
diretório que será utilizado para áreas temporária e que exista área suficiente para os arquivos
temporários. Caso um GPF aconteça libere o maximo de espaço em disco na área temporária e rode o
relatório novamente.

Obs.

A variavel do Oracle.ini REPORTS20_TMP não funciona, é necessario a variavel de DOS


(TMP=<DIRETORIO>).

4.7.2- Loops infinitos detectados

Se seu relatório detectar loop infinito verifique se não existe alguma frame de tamanho fixo com um
objeto interno expansível (frames, text itens) que possa vazar a área da frame mais externas. Caso isso
aconteça um loop infinito pode ser gerado.

4.7.3- Problemas de impressão - Áreas Negras

Se em seu report alguma frame sair totalmente negra ou “sólida” , verifique o driver da impressora
testando com outra impressora/driver. Muitas vezes alguma frame não transparente (branca) sai negra
bloqueando conteúdo de campos em drives de impressão mais antigos.

4.7.4- Problemas de impressão - Imagens

Se uma imagem em seu report não sair no relatório, procure verificar utilizando outros drivers de
impressão. Pode não haver memória suficiente para um impressão.

Para detectar o problema de impressão de imagens, gere o relatório para um arquivo gerando via driver
de impressora postscript e analise o arquivo gerado via um editor ou comando type. Uma impressão
postscrip é um arquivo texto com os comandos postscrips em forma de texto, deste modo um erro de falta
de memória na formatação da pagina é muitas vezes indicado dentro do mesmo, procure pela palavra
ERROR ou IMAGE.

Observação:
Outros erros menos comuns estão catalogados no CD-ROM “Oracle Suport Notes”.

66
5- PREVENÇÃO DE GPF’S
GPFs podem ocorrer se recursos do windows (memória, pilha interna, etc..) chegarem a níveis muito
baixos, por exemplo quando muitas aplicações estiverem abertas simultaneamente.

Feche todas as aplicações windows não necessárias e evite abrir e fechar a mesma aplicação varias vezes.
Algumas aplicações não liberam todos os recursos utilizados pelo windows ao seu término, deste modo
em cada utilização um pouco dos recursos é perdido.

5.1- COMO MINIMIZAR A UTILIZAÇÃO DE MEMÓRIA/RECURSOS DO


WINDOWS.

1. Evite no program manager icones e grupos não necessários.


2. Não mantenha aplicações rodando. Mesmo minimizadas estas aplicações gastam recursos do
windows.
3. Diminua o número de fontes carregadas no windows, alem de poupar recursos acelera na carga do
windows. Verifique a seção [FONTS] de seu WIN.INI, não carregue mais de 100 fontes.
4. Evite utilizar wallpaper complexos com muitas cores em seu desktop, eles consomem memória.
5. Procure manter drivers de vídeo e rede atualizados.
6. Evite a existencia de DLLs duplicadas em seu disco. Isto eliminará que um programa acesse DLLs de
versões anteriores.
7. OLE 2.0 consome grande quantidade de recursos em seu sistema, minimize seu uso quando estiver
utilizando aplicações oracle.
8. Garanta no mínimo 450k de memória convencional ao carregar o windows.

5.2 - O QUE FAZER SE UM GPF ACONTECER


1. Feche todas as aplicações e saia do windows, se possível reinicialize a maquina. No momento que
uma aplicação windows cai por GPF ela sai sem “limpar” o ambiente, deste modo seu sistema fica
com vários controles internos e recursos ainda alocados, sair do windows é a única forma de
garantir uma limpeza deste ambiente.

2. Tente isolar o problema, rode a aplicação de outra maquina e se possível com outra chave de acesso.
Caso o erro não aconteça o problema é do seu ambiente, caso o erro se repita provavelmente é
algo em sua aplicação.

5.3- SE O ERRO FOR DE AMBIENTE


1. Rode um scandisk em sua máquina para detectar alguma estrutura de arquivo corrompida. Arquivos
cross-linked podem causar GPF.

2. Compare os arquivos ORACLE.INI utilizados para possiveis diferenças

3. Se com a mesma chave ocorre o erro e com chaves diferentes não verifique permissão de gravação em
diretórios da rede.

4. Verifique os diretórios temporários se estão corretamente configurados e se o espaço em disco é


suficiente.

67
5. No DOS Utilize os comandos
SET TEMP=C:\TEMP onde C:\TEMP é um diretório valido.
SET TMP=C:\TEMP

1. E no arquivo ORACLE.INI verifique as variáveis


CA_UPREFS para arquivo de preferencias
REPORTS_TMP para temporario do report

1. Compare os arquivos config.sys e autoexec.bat a fim de detectar diferenças em drivers carregados.

2. Remova se existir o arquivo SHARE.EXE de seu autoexec.bat. O driver deve estar carregado no
windows via arquivo SYSTEM.INI seção [386Enh] como vshare.386.
Exemplo:

SYSTEM.INI
----------
...
[386Enh]
display=*vdd,*vflatd
WinExclusive=0
...
device=vshare.386 <-Substituto do SHARE.EXE
...
Garanta que o arquivo vshare.386 existe no seu diretório windows/system.

1. No CONFIG.SYS inclua as linhas

BUFFERS=60,0
FILES=60

5.4- SE O ERRO FOR DA APLICAÇÃO


1. Isole a trigger causadora do erro rodando o forms em debug mode

2. Detectada a trigger isole a procedure/código PL/SQL causador do erro detectando via


MESSAGE/SYNCHRONIZE a parte do código geradora do erro.

3. Se a rotina for muito grande tente dividi-la em bloco menores

4. Comente o código de modo a ir rodando a aplicação e liberando o código até se detectar os


comandos/sql causador do problema.

5. Caso o código isolado não apresente nenhum problema aparente, contate o suporte de modo a
verificar algum bug ou problema mais complexo.

68
6- HOMOLOGANDO UM APLICATIVO ORACLE

6.1- ANALISE DA LÓGICA UTILIZADA


É importante que os desenvolvedores procurem sempre utilizar uma boa lógica dentro do fonte de seus
aplicativos. A experiência demonstra que 70 % dos problemas de performance encontrados são
decorrentes de uma má lógica e de comandos SQL problemáticos, apesar de sintaticamente corretos.

É necessário manter uma meta de se obter códigos eficientes do ponto de vista de performance e fontes
claros do ponto de vista de futuras manutenções.

Listamos a seguir alguns pontos que devem ser verificados pelo analista responsável no momento da
analise de um programa SQL, quer seja um batch pl/sql ou códigos de procedures e functions dentro de
forms e reports.

6.1.1- Processamento convencional vs. Processamento relacional.

Um grande hábito que vem da época de outros ambientes e banco de dados é de se programar em um
banco relacional como se estivesse programando arquivos seqüenciais indexados.

Exemplo Lógica 1 .

1. Varre- se os registros da tabela 1 em determinada condição A.


2. Para cada registro da tabela 1 inicie uma varredura em todos registros da tabela 2 onde código seja igual
ao código da tabela 1.
3. Para cada registro da tabela 2
4. Procure tradução de código 1 em tabela 3.
5. Procure tradução de código 2 em tabela 4.
6. Imprima a linha do registro processado.

Esta lógica pode ser usada em qualquer ambiente, de um Dbase até um Cobol/CICS passando pelo
PL/SQL, não é portanto uma lógica otimizada para os recursos que um banco relacional proporciona.

Supondo que :

· Tabela 1 tenha dentro da condição A um número de 500 linhas,


· Tabela 2 tenha em média 1.000 linhas correspondentes a tabela 1.

Temos que nos itens 4 e 5 um total de 500.000 cursores serão abertos e fechados pelo oracle, o que trará
problemas de performance alem de impactar fortemente o tráfego da rede.

O ambiente relacional permite que se “crie” através de um join a tabela perfeita para sua aplicação dentro
de seu programa. Esta mudança minimiza o processamento pelo oracle , diminui o tráfego na rede e
simplifica bastante a lógica do programa evitando assim erros de lógica e uma maior facilidade para
manutenção posterior.

69
Exemplo Lógica 2.

1. Crie um cursor Selecionando dados da tabela 1, tabela 2, tabela 3 e tabela 4


onde condição A seja aplicada sobre tabela 1 e
código1 da tabela 1 igual a código da tabela 2
e código 1 da tabela 2 igual a código da tabela 3
e código 2 da tabela 2 igual a código da tabela 4.
1. Varre- se os registros do cursor aberto em 1
2. Para cada registro Imprima a linha do registro processado.

A mudança da lógica leva quase todo o processamento para o passo 1 (Abertura do cursor), este
processamento é enviado de uma única vez ao oracle e somente 1 único cursor é aberto. Seu fonte se
torna menor, mais claro e eficiente.

6.1.2- Verificando transações.

Outra preocupação que os desenvolvedores devem ter é em relação a integridade dos dados em caso de
falha. É importante que os posicionamentos de commits não criem transações falhas e inconsistências na
base de acordo com as regras definidas para o sistema (Vide 1.7 Pag. 21).

Este cuidado deve ser aplicado em programas PL/SQL e em forms com mais de uma tela compondo uma
única transação.

Para testar seus programas aconselhamos: Que durante os teste de um PL/SQL, ou forms, mate-se a
sessão no servidor, ou desligue-se o micro no meio de um processamento. Este procedimento não poderá
acarretar nenhuma inconsistência na base ou problemas com campos de controle, já que é algo normal de
se acontecer no ambiente de produção e em equipamentos de usuário.

6.1.3- Lógica de restart.

O Oracle não possui recursos automáticos de restart. É de competência do programador incluir esta lógica
em seus programas.

Os programas batch necessitam ter uma lógica de restart. No caso de uma falha do processamento, o
sistema tem que recomeçar do momento que parou. Evitando gastos de recursos necessários e volta de
backup para bases parcialmente atualizadas, sem o controle do que já foi processado.

Para testar um programa batch mate a sessão no meio do processamento e reinicie o mesmo a fim de
validar as rotinas de restart.

6.1.5- Sempre verificar se execução de comandos foi bem sucedida.

Outro erro muito comum em aplicações é assumir que um comando irá dar certo, quer seja traduzindo um
campo de uma tabela básica via select, uma atualização via update , obtenção de uma taxa, etc..

Tenha como regra que qualquer comando SQL pode falhar. Consequentemente, os programas tem que
estar preparados para isso.

70
Um processamento batch de 20.000 linhas pode ser interrompido, caso uma única linha encontre um
código cuja tradução, inesperadamente, provoque uma exception NO_DATA_FOUND. Proteja todos os
comandos SQL com tratamento de exceptions, de modo que todo e qualquer erro seja tratado, evitando
uma interrupção não esperada.

Cuidado com a trigger WHEN_OTHERS. Informe no log o código de erro (SQLCODE) e a mensagem
correspondente (SQLERRM).

6.1.6- Aplicando os conceitos

Baseados nos tópicos apresentados, podemos analisar o exemplo fornecido como padrão de codificação
PL/SQL.

Programa 1: Código no padrão porem com lógica a ser melhorada.

SPOOL &1
BEGIN
--
-- Programa FNM003 - Calculo de reajuste de funcionarios. - Logica 1
--
--Este programa varre a tabela de funcionarios atualizando o salario de acordo com índice encontrado na tabela de cargos,
-- caso este índice não seja encontrado o funcionario não tera aumento.
--
--
-- Historico do programa
-- Data Autor Mudancas
-- -----------------------------------------------------------------------------------------------------------
-- 01/04/95 Antonio Jose Entrega de programa para produção
--01/08/95 Antonio Jose Mudanca de acesso para update via ROWID
--01/12/95 Claudia Silva Exception NO_DATA_FOUND para tab_cargo incluido (sem taxa)
--
-- Rotinas utilizadas no programa ( Declaração foward ) -----------------------------------------------------------

FUNCTION calcula_taxa( par_cod_cargo IN NUMBER, -- Calcula a taxa de reajuste para um cargo


par_cod_depto IN NUMBER) -- de determinado departamento
RETURN NUMBER;

PROCEDURE atualiza_sal( par_cod_rowid IN ROWID, -- Atualiza o salario do funcionario apontado


par_val_sal IN NUMBER); -- pelo ROWID.

-- Declaração de Cursores e variaveis globais ------------------------------------------------------------------------

CURSOR cur_tab_func IS
SELECT cod_func, val_salario,cod_cargo,cod_depto, ROWID
FROM tab_func;

wrk_val_taxa NUMBER;
wrk_val_sal_novo NUMBER;

71
--Implementação de procedures e functions ------------------------------------------------------------------------

PROCEDURE atualiza_sal ( par_cod_rowid IN ROWID, par_val_sal IN NUMBER) is


BEGIN
UPDATE tab_func
SET
val_salalrio = par_val_sal
WHERE
ROWID = par_cod_rowid;
EXCEPTION
WHEN OTHERS THEN
RAISE_APLICATION_ERROR(200001,’Erro atualizando salario na tabela func’);
END atualiza_sal;
-------------------------------------------------------------------------------------------------------------------------
FUNCTION calcula_taxa ( par_cod_cargo IN NUMBER, par_cod_depto IN NUMBER)
RETURN NUMBER is

wrk_ind_reajuste NUMBER;

BEGIN
SELECT
val_ind_reaj INTO wrk_ind_reajuste
FROM
tab_cargos
WHERE
cod_cargo = par_cod_cargo AND
cod_depto = par_cod_depto;

RETURN wrk_ind_reajuste;
EXCEPTION
WHEN NO_DATA_FOUND THEN
return 1;
WHEN OTHERS THEN
RAISE_APLICATION_ERROR(200002,’Erro acessando tabela de cargos’);
END calcula_taxa;

--------- Corpo principal do programa -------------------------------------------------------------------------------


BEGIN
FOR wrk_reg IN cur_tab_func LOOP
wrk_val_taxa := calcula_taxa(wrk_reg.cod_cargo, wrk_reg.cod_depto);
wrk_val_sal_novo := wrk_val_taxa * wrk_reg.val_salario;
atualiza_sal ( wrk_reg.ROWID, wrk_val_sal_novo);
END LOOP;
COMMIT;
END;
/
EXIT SQL.SQLCODE
/

Observa-se os seguintes problemas na lógica

1. Para cada linha de cursor cur_tab_func um select é utilizado para calcular a taxa.
2. Possui uma transação muito longa, sem commits intermediários, o que alem de manter os locks por
mais tempo, pode acarretar problemas de roolback.
3. Não possui lógica de restart.

72
Podemos reestruturá-lo da seguinte forma:

Programa 2
SPOOL &1
BEGIN
--
-- Programa FNM003 - Calculo de reajuste de funcionarios. - Logica 1
--
--Este programa varre a tabela de funcionarios atualizando o salario de acordo com índice encontrado na tabela de cargos,
-- caso este índice não seja encontrado o funcionario não tera aumento.
--
-- Tabelas Acessadas Leitura Update Delete Insert
--TAB_FUNC x x
--TAB_CARGOS x
--TAB_CONTROLE x x
--
-- Historico do programa
-- Data Autor Mudancas
-- -----------------------------------------------------------------------------------------------------------
-- 01/04/95 Antonio Jose Entrega de programa para produção
--01/08/95 Antonio Jose Mudanca de acesso para update via ROWID
--01/12/95 Claudia Silva Exception NO_DATA_FOUND para tab_cargo incluido (sem taxa)
--0105/96 Antonio Carlos Mudanca da logica para join, commit a cada 200 registros e lógica de reestart
--
-- Rotinas utilizadas no programa ( Declaração foward ) -----------------------------------------------------------

PROCEDURE commita_bloco( par_cod_func IN NUMBER); -- Commita um bloco de atualizações

PROCEDURE atualiza_sal( par_cod_rowid IN ROWID, -- Atualiza o salario do funcionario apontado


par_val_sal IN NUMBER); -- pelo ROWID.

-- Declaração de Cursores e variaveis ------------------------------------------------------------------------

wrk_num_linhas NUMBER; -- Variavel usada para controlar o tamanho da transação.


wrk_cod_func_reestart NUMBER; -- Ultimo funcionario processado.

CURSOR cur_tab_func IS – Através de Join obtenho o novo salário.


SELECT tab_func.cod_func, ,
( tab_func.val_salario * nvl(tab_cargos.val_ind_reaj,1)) val_sal_novo
, ROWID
FROM tab_func, tab_cargos
WHERE
tab_func.cod_cargo = tab_cargo.cod_cargo AND
tab_func.cod_depto = tab_cargo.cod_depto AND
tab_func.cod_func > glb_cod_func_reestart; -- So processo códigos não processados.
ORDER BY 1;

--Implementação de procedures e functions ------------------------------------------------------------------------

PROCEDURE atualiza_sal ( par_cod_rowid IN ROWID, par_val_sal IN NUMBER) is


BEGIN
UPDATE tab_func
SET
val_salalrio = par_val_sal
WHERE
ROWID = par_cod_rowid;
EXCEPTION
WHEN OTHERS THEN
RAISE_APLICATION_ERROR(200001,’Erro atualizando salario na tabela func’);
END atualiza_sal;
-------------------------------------------------------------------------------------------------------------------------

73
-----------------------------------------------------------------------------------------------------------------------------------------------------------
PROCEDURE commita_bloco ( par_cod_func IN NUMBER) is
wrk_ind_reajuste NUMBER;
BEGIN
UPDATE tab_controle -- Tabela só tem um registro.
SET cod_func_reestart = par_cod_func; -- Atualizo o último funcionario processado.
Commit; -- Fecho a transação
EXCEPTION
WHEN OTHERS THEN
RAISE_APLICATION_ERROR(200002,’Erro commitando bloco’);
END commita_bloco;

--------- Corpo principal do programa -------------------------------------------------------------------------------

BEGIN
-- Inicializo variavel de restart ---------

SELECT nvl(cod_func_reestart,0) INTO glb_cod_func_reestart


FROM tab_controle;
--
-- Se glb_cod_func > 0 só significa que estou reestartando um processo que terminou anormalmente.
--

wrk_num_linhas:=0;
FOR wrk_reg IN cur_tab_func LOOP
atualiza_sal ( wrk_reg.ROWID, wrk_val_sal_novo);
wrk_num_linhas:= wrk_num_linhas + 1;
IF wrk_num_linhas > 200 THEN
commita_bloco(wrk_reg.cod_func);
wrk_num_linhas:=0;
END IF;
END LOOP;

-- Programa terminou OK, reseto o controle de reestart

UPDATE tab_controle -- Tabela só tem um registro.


SET cod_func_reestart = NULL; -- Isto indica que o processo terminou OK.

COMMIT;
END;
/
EXIT SQL.SQLCODE
/

Como podemos observar, a lógica ficou menor. Temos implementado um bloco menor de transação
assim, como um controle de restart em caso de interrupção anormal do sistema.

74
6.2- CLASSIFICANDO OS MÓDULOS DE SUA APLICAÇÃO
É atribuição do analista responsável classificar os módulos de seu sistema nas classificações: baixa,
média e alta criticidade . Estes critérios irão permitir um melhor foco na fase de testes e homologação
das aplicações. Sugerimos abaixo critérios para a classificação dos módulos.

Tipo A - Baixa Criticidade


· Manutenção de tabelas.
· Programas batchs de limpeza de tabelas.
· Listagens de cadastros.
· Módulos cujo uso não seja de rotinas do dia a dia.
· Módulos não impactantes para o andamento do negócio.
· Módulos não utilizados por órgãos de gerência.
· Módulos que possam ter sua execução adiada sem prejuízos para a rotina operacional da empresa.

Os testes devem consistir de:


· Verificar se regras de negócio e a funcionalidade definida para a módulo estão de acordo com o
produto apresentado.

Tipo B - Média Criticidade


· Módulos on-line de uso no dia a dia
· Módulos utilizados por áreas gerênciais da empresa.
· Processos batchs.
· Relatórios de conferencia.
· Módulos que podem impactar o dia a dia dos negócios dentro do ambiente <cliente>.

Os testes devem consistir de:


· Verificar se regras de negócio e a funcionalidade definida para a módulo estão de acordo com o
produto apresentado.
· Preenchimento das “Ficha de Controle de Qualidade - Aplicativos Oracle”.
· Utilização de explain sobre as queries mais importantes ( vide item 1.5.1 Pag 17).

Tipo C - Alta Criticidade


· Módulos on-line de processos vitais para a empresa.
· Aplicações que acessem tabelas cujo os nomes estejam na relação de tabelas críticas fornecida pelos
ADs.
· Processos batchs que necessitem rodar diariamente.
· Relatórios de emissão de documentos legais, notas fiscais et.
· Qualquer módulo cujo não funcionamento paralise as atividades operacionais da empresa.

Os testes devem consistir de:

· Verificar se regras de negócio e a funcionalidade definida para a módulo estão de acordo com o
produto apresentado.
· Preenchimento das “Ficha de Controle de Qualidade - Aplicativos Oracle”.
· Utilização de explain sobre as queries mais importantes ( vide item 1.5.1 Pag 17).
· Verificação pelo analista responsável da lógica utilizada no programa.
· Utilização de TRACE/Tkprof dentro do ambiente de homologação acompanhado pelo AD/DBA
( vide item 1.5.2 Pag 18).

75
6.3- VERIFICANDO CONCEITOS APLICADOS
Aplicações definidas como tipo B e C devem ser validadas via ficha de Controle de Qualidade de
Aplicativos Oracle. Estas fichas contem um resumo das regras definidas neste documento e devem ser
preenchidas para cada módulo das aplicações a serem validadas.

6.3.1- Estrutura básica das fichas:

1) Cabeçalho da Ficha

Ficha de Controle de Qualidade - Aplicativos Oracle


Ambiente <cliente>

Aplicação Módulo: Cód. DGP:


Descrição:
Responsável: Empresa:
Data: Obs:

Este cabeçalho deve ser preenchido com dados sobre o módulo verificado.

2) Corpo da Ficha

Atende
Atende parcialmente
Não atende
Peso Conceito Pag.
Conceitos Gerais
Crie índices da coluna mais seletiva para a menos seletiva 10

Deve ser preenchido para cada critério especificado, onde cada coluna tem o seguinte significado:

· Peso

Esta coluna informa o peso do conceito para o ambiente <cliente>. Pode assumir os seguintes valores:

1 - Regra Obrigatória de ser observada


2 - Regra fortemente indicada
3 - Regra desejável

· Conceito

Resumo do conceito a ser observado no módulo, para maiores informações vide documento na pagina
indicada pela coluna PAG.

· Não Atende

Marque um X nesta coluna caso o módulo não atenda o item, indo contra o conceito especificado.

76
· Atende Parcialmente

Marque um X nesta coluna caso o módulo atenda parcialmente o item, com poucas ocorrências de
códigos que vão contra o conceito especificado.

· Atende

Marque um X nesta coluna caso o módulo atenda o item. Não existindo código que atue contra o conceito
especificado.

· Pag.

Esta coluna indica a página no documento onde se encontra uma explicação para o conceito indicado.

Observações:

· Deixe a linha em branco caso um critério não se aplique ao módulo em questão.


· O fim de cada ficha deve ser rubricado pelo responsável.
· Existem 3 conjuntos de fichas: 1 para aplicativos PL/SQL, 1 para FORMS e 1 para REPORTS. As
fichas de reports e forms incluem os conceitos de SQL.
· Não é obrigatório que se atenda todos os critérios para a passagem do módulo para produção. A
decisão fica a cargo do analista responsável, sendo as fichas um roteiro para auxiliá-lo.

77
EXEMPLO DE FICHAS DE CONTROLE DE QUALIDADE - APLICATIVOS ORACLE

78
Ficha de Controle de Qualidade - Aplicativos Oracle
Ambiente <cliente>

Aplicação Módulo: Cód. DGP:


Descrição:
Responsável: Empresa:
Data: Obs:

Avaliação de Critérios de programação - PL/SQL - Pag. 1/2

Atende
Atende parcialmente
Não atende
Peso Conceito Pag.
Conceitos Gerais
1 Deve se criar índices pelas foreignkeys de uma tabela. 10
1 Nas escolha de qual coluna deve ser utilizado em um índice escolha primeiro a coluna mais 10
freqüentemente utilizada em clausula where.
2 Uso database hints deve sempre acompanhado pelo AD/DBA , numa view utilize codificados 20
diretamente em seu fonte.
2 Utilize Explain plan para queries complexas 17
2 Utilize TRACE/TKPROF em ambiente de homologação para programas tipo críticos. 18
3 Crie índices da coluna mais seletiva para a menos seletiva 11
SQL
1 Clausula having somente para colunas calculadas via funções de coluna 15
1 Informe todas as ligações entre tabelas 13
1 Não armazenar rowid para uso em sessões posteriores 14
1 Sempre qualifique as colunas quando mais de uma tabela for envolvida 11
1 Usar order by quando for obrigatório resultado ordenado 13
2 Evitar operações dentro de funções de coluna 12
2 Evite DISTINCT 12
2 Evite between 16
2 Evite in (a,b,c) 16
2 Evite not in, utilize exists 16
2 Evite subselects, utilize joins 16
2 Evite where “CAMPO IS NULL” - não utilizara o índice 14
2 Armazene rowid para acesso a tabelas numa mesma sessão. 14
3 Agrupe primeiramente as condições de ligação entre tabelas e depois as condições de filtros 12
individuais em cláusulas WHERE.
3 Na clausula FROM apresente as tabelas na ordem das tabelas com mais linhas para as com 12
menos linhas
3 Criar apelidos (alias) para expressões 11
3 Procure utilizar sempre toda a chave de um índice composto 13
3 Se possível não aplique funções/Expressões em colunas da tabela 13
3 Use parênteses na clausula where 14
3 Uso do comando like pode não utilizar índices 13

Visto Responsável :

79
Avaliação de Critérios de programação - PL/SQL - Pag. 2/2

Atende
Atende parcialmente
Não atende
Peso Conceito Pag.
PL/SQL
1 Defina bem o conjunto mínimo de ações de uma transação 22
1 Evite dead locks atualizando tabelas sempre na mesma ordem 23
1 Feche os cursores quando não mais forem utilizados 33
1 Para comandos explícitos de lock consulta ao AD/DBA 23
1 Não utilizar commit em cursor for update 33
1 Não utilizar database hints em PL/SQL de FORMS e REPORT 20
1 Não utilize select from dual para utilizar funções SQL no PL 24
2 Estruture bem seus programas. Utilize procedures e functions 26
2 Evite abrir cursores dentro de loops de outros cursores 33
2 Evite transações muito curtas 23
2 Evite transações muito longas 22
2 Não faça grandes blocos PL/SQL, utilize códigos menores, menos de 100 linhas 26
2 Não use locks com cursor for update 34
2 Utilize foward declarations para procedures 27
2 Utilize operadores %TYPE e %ROWTYPE 26
2 Utilizar os mesmos tipos dados existentes no database para as variáveis: 25
3 Procure usar a estrutura de cursor loop 34
3 Utilize conversão explicita para variáveis 26
3 Utilize cursor explicito uma vez que é mais eficiente que utilizar cursor implicitamente 33
3 Padronize seu fonte PL/SQL 36

Visto Responsável :

80
Ficha de Controle de Qualidade - Aplicativos Oracle
Ambiente <cliente>

Aplicação Módulo: Cód. DGP:


Descrição:
Responsável: Empresa:
Data: Obs:

Avaliação de Critérios de programação - FORMS Pag. 1/3

Atende
Atende parcialmente
Não atende
Peso Conceito Pag.
Conceitos Gerais
1 Deve se criar índices pelas foreignkeys de uma tabela. 10
1 Nas escolha de qual coluna deve ser utilizado em um índice escolha primeiro a coluna mais 10
freqüentemente utilizada em clausula where.
2 Uso database hints deve sempre acompanhado pelo AD/DBA , numa view utilize codificados 20
diretamente em seu fonte.
2 Utilize Explain plan para queries complexas 17
2 Utilize TRACE/TKPROF em ambiente de homologação para programas tipo críticos. 18
3 Crie índices da coluna mais seletiva para a menos seletiva 11
SQL
1 Clausula having somente para colunas calculadas via funções de coluna 15
1 Informe todas as ligações entre tabelas 13
1 Não armazenar rowid para uso em sessões posteriores 14
1 Sempre qualifique as colunas quando mais de uma tabela for envolvida 11
1 Usar order by quando for obrigatório resultado ordenado 13
2 Evitar operações dentro de funções de coluna 12
2 Evite DISTINCT 12
2 Evite between 16
2 Evite in (a,b,c) 16
2 Evite not in, utilize exists 16
2 Evite subselects, utilize joins 16
2 Evite where “CAMPO IS NULL” - não utilizara o índice 14
2 Armazene rowid para acesso a tabelas numa mesma sessão. 14
3 Agrupe primeiramente as condições de ligação entre tabelas e depois as condições de filtros 12
individuais em cláusulas WHERE.
3 Na clausula FROM apresente as tabelas na ordem das tabelas com mais linhas para as com 12
menos linhas
3 Criar apelidos (alias) para expressões 11
3 Procure utilizar sempre toda a chave de um índice composto 13
3 Se possível não aplique funções/Expressões em colunas da tabela 13
3 Use parênteses na clausula where 14
3 Uso do comando like pode não utilizar índices 13

Visto Responsável :

81
Avaliação de Critérios de programação - FORMS Pag. 2/3

Atende
Atende parcialmente
Não atende
Peso Conceito Pag.
PL/SQL
1 Defina bem o conjunto mínimo de ações de uma transação 22
1 Evite dead locks atualizando tabelas sempre na mesma ordem 23
1 Feche os cursores quando não mais forem utilizados 33
1 Para comandos explícitos de lock consulta ao AD/DBA 23
1 Não utilizar commit em cursor for update 33
1 Não utilizar database hints em PL/SQL de FORMS e REPORT 20
1 Não utilize select from dual para utilizar funções SQL no PL 24
2 Estruture bem seus programas. Utilize procedures e functions 26
2 Evite abrir cursores dentro de loops de outros cursores 33
2 Evite transações muito curtas 23
2 Evite transações muito longas 22
2 Não faça grandes blocos PL/SQL, utilize códigos menores, menos de 100 linhas 26
2 Não use locks com cursor for update 34
2 Utilize foward declarations para procedures 27
2 Utilize operadores %TYPE e %ROWTYPE 26
2 Utilizar os mesmos tipos dados existentes no database para as variáveis: 25
3 Procure usar a estrutura de cursor loop 34
3 Utilize conversão explicita para variáveis 26
3 Utilize cursor explicito uma vez que é mais eficiente que utilizar cursor implicitamente 33
3 Padronize seu fonte PL/SQL 36
FORMS
1 Bloqueie as consultas em campos não definidos junto ao usuário como passíveis de consultas 52
1 Empacote códigos comuns em procedures 41
1 Não mesclar em uma única LIB rotinas especificas para forms e report 43
1 Não processe manualmente nenhum comando de DML na tabela driver dentro de um bloco 52
base-table (INSERT, DELETE ou UPDATE)
1 Não utilize LOVs para mais de 3000 linhas de retorno. 47
1 Não utilize nomes de procedures e objetos do forms com o mesmo nome de objetos do banco 41
de dados
1 Nunca popule uma tabela base table de outra forma que não seja via built-in 52
EXECUTE_QUERY
1 Utilize a rotina exec_form_menu para chamar um forms de um menu ou outro forms 53
1 Não faça libs muito grandes, divida em libs menores com procedimentos afins 43

Visto Responsável :

82
Avaliação de Critérios de programação - FORMS Pag. 3/3

Atende
Atende parcialmente
Não atende
Peso Conceito Pag.
FORMS (cont)
2 Não utilize a técnica de se trabalhar com bloco não base-table e se carregar as linhas das 51
tabelas utilizando-se de /SQL
2 Antes de se chamar um novo forms emita um comando POST 54
2 Evite a built-in LAST_RECORD. 51
2 Evite triggers muito grandes 40
2 Não utilize trigger Post-change 49
2 Não crie em um único forms várias windows e várias funções distintas 41
2 Referencie a objetos do forms prefixando o nome do bloco 41
2 Utilize a trigger DO-KEY para disparar ações correspondentes a teclas ao invés de ativar as 49
built-ins diretamente
2 Utilize opção long list para LOVs com mais de 200 linhas 47
2 Não crie procedure ou trigger com mais de 100 linhas 41
3 Evite a utilização de SYSDATE a toda hora 46
3 Evite acessar diretamente dados externos em procedures, utilize os parâmetros IN e OUT 42
3 Forms deve estar preparado para tratar eventos possíveis 40
3 Mantenha seus códigos pl/sql organizados em procedures e functions 40
3 Não use auto-refresh em LOVs 48
3 Use LOV para validação quando o número de linhas retornada for menor que 200 linhas 48
3 Prefira variáveis de canvas null a variáveis globais 44
3 Use parâmetros para passar informações entre forms 45
3 Utilize MESSAGE() junto com SYNCHRONIZE, 42
3 Utilize sempre que possível as opções DEFERRED=TRUE e AUTO-QUERY=TRUE em uma 52
relação
3 Utilize somente as cores das 2 colunas mais a esquerda da pallete de cores 42

Visto Responsável :

83
Ficha de Controle de Qualidade - Aplicativos Oracle
Ambiente <cliente>

Aplicação Módulo: Cód. DGP:


Descrição:
Responsável: Empresa:
Data: Obs:

Avaliação de Critérios de programação - REPORT Pag. 1/2

Atende
Atende parcialmente
Não atende
Peso Conceito Pag.
Conceitos Gerais
1 Deve se criar índices pelas foreignkeys de uma tabela. 10
1 Nas escolha de qual coluna deve ser utilizado em um índice escolha primeiro a coluna mais 10
freqüentemente utilizada em clausula where.
2 Uso database hints deve sempre acompanhado pelo AD/DBA , numa view utilize codificados 20
diretamente em seu fonte.
2 Utilize Explain plan para queries complexas 17
2 Utilize TRACE/TKPROF em ambiente de homologação para programas tipo críticos. 18
3 Crie índices da coluna mais seletiva para a menos seletiva 11
SQL
1 Clausula having somente para colunas calculadas via funções de coluna 15
1 Informe todas as ligações entre tabelas 13
1 Não armazenar rowid para uso em sessões posteriores 14
1 Sempre qualifique as colunas quando mais de uma tabela for envolvida 11
1 Usar order by quando for obrigatório resultado ordenado 13
2 Evitar operações dentro de funções de coluna 12
2 Evite DISTINCT 12
2 Evite between 16
2 Evite in (a,b,c) 16
2 Evite not in, utilize exists 16
2 Evite subselects, utilize joins 16
2 Evite where “CAMPO IS NULL” - não utilizara o índice 14
2 Armazene rowid para acesso a tabelas numa mesma sessão. 14
3 Agrupe primeiramente as condições de ligação entre tabelas e depois as condições de filtros 12
individuais em cláusulas WHERE.
3 Na clausula FROM apresente as tabelas na ordem das tabelas com mais linhas para as com 12
menos linhas
3 Criar apelidos (alias) para expressões 11
3 Procure utilizar sempre toda a chave de um índice composto 13
3 Se possível não aplique funções/Expressões em colunas da tabela 13
3 Use parênteses na clausula where 14
3 Uso do comando like pode não utilizar índices 13

Visto Responsável :

84
Avaliação de Critérios de programação - REPORT Pag. 2/2

Atende
Atende parcialmente
Não atende
Peso Conceito Pag.
PL/SQL
1 Defina bem o conjunto mínimo de ações de uma transação 22
1 Evite dead locks atualizando tabelas sempre na mesma ordem 23
1 Feche os cursores quando não mais forem utilizados 33
1 Para comandos explícitos de lock consulta ao AD/DBA 23
1 Não utilizar commit em cursor for update 33
1 Não utilizar database hints em PL/SQL de FORMS e REPORT 20
1 Não utilize select from dual para utilizar funções SQL no PL 24
2 Estruture bem seus programas. Utilize procedures e functions 26
2 Evite abrir cursores dentro de loops de outros cursores 33
2 Evite transações muito curtas 23
2 Evite transações muito longas 22
2 Não faça grandes blocos PL/SQL, utilize códigos menores, menos de 100 linhas 26
2 Não use locks com cursor for update 34
2 Utilize foward declarations para procedures 27
2 Utilize operadores %TYPE e %ROWTYPE 26
2 Utilizar os mesmos tipos dados existentes no database para as variáveis: 25
3 Procure usar a estrutura de cursor loop 34
3 Utilize conversão explicita para variáveis 26
3 Utilize cursor explicito uma vez que é mais eficiente que utilizar cursor implicitamente 33
3 Padronize seu fonte PL/SQL 36
REPORTS
1 Utilizar na trigger BEFORE-FORM o comando DBMS_SESSION.SET_ROLE(‘ALL’) 60
1 Defina o mínimo de queries possíveis 57
1 Informe apelidos para as colunas que existirem em mais de uma query 60
1 Só selecione campos no data model que serão utilizados no LAYOUT 60
1 Utilizar o máximo de processamento e transformação de colunas dentro das queries 60
2 Evite o uso da built-in SRW.DO_SQL dentro de PL/SQL 61
2 Não utilize frames e objetos sólidos da cor branca 61
3 Evite truncar campos no LAYOUT 60
3 Evite utilizar a opção total de páginas em seu relatório ( Ex: Pagina 1 de 30 ) 61
3 Retire frames desnecessárias geradas pelo default LAYOUT 61

Visto Responsável :

85
6.4- DEFININDO TEMPOS ACEITÁVEIS
Deve-se no fase de homologação preencher a Ficha de Medição de Tempos - Aplicativos Oracle.

Este documento é importante pois permitirá direcionar os trabalhos de otimização de performance sobre
os aplicativos/tarefas que possam impactar o bom andamento do sistema como um todo.

É importante observar os seguintes tópicos:

· Efetuar no mínimo 3 medições de tempos em horas e dias diferentes de modo a se obter uma média
mais realista do dia a dia em produção. Uma única medida pode ter seu resultado distorcido
devido a condições atípicas do ambiente Oracle/Risc no momento da medição.

· Nunca efetue uma medição logo após o banco Oracle entrar no ar, espere pelo menos 6 horas após o
startup para efetuar a medição. É necessário um tempo de estabilização para que a área de cache
do banco de dados seja preenchida, evitando-se resultados piores devido ao maior numero de I/O
em disco que seria necessário.

· Para processos compostos de várias tarefas indique um * na coluna G (Grupo) e indique na descrição
do mesmo as seqüência de módulos/tarefas que compõem este grupo.

Ex:

Tempo Aceitável para produção


Número de medições
Tempo Médio em ambiente de homologação
Seq G Tipo Módulo Data Visto
1 * SHL CARG005.SH Carga movimento IBM (Seq 2-5) 01:0
0
2 TRN FTP ARQUIVO BCARG004.DAT 00:0 3 00:0
3 5
3 LDR BCARG004.CTL 00:1 3 00:1
0 5
4 PLS APLFN004.SQL 00:2 3 00:2
5 0
5 REP RELCRIT04.REP 00:1 3 00:2
7 0
6

Isto permite identificar que apesar do tempo do PL/SQL ter ultrapassado o esperado, no conjunto o
processo de carga continua aceitável, abaixo de 1 hora (55 minutos).

· Todos as medições devem ser rubricadas pelo responsável.

· Caso o módulo seja do tipo OUT (Outros) especificar na descrição o tipo do processo.

86
Exemplo de Ficha de Medição de Tempos - Aplicativos Oracle

87
Ficha de Medição de Tempos - Aplicativos Oracle
Ambiente <cliente>

Módulo: Folha : de
Descrição:
Responsável: Empresa:
Data: Obs:

Tempos Observados em Ambiente de Homologação


Tempo Aceitável para produção
Número de medições
Tempo Médio em ambiente de homologação
Seq G Tipo Módulo Data Visto
1 *
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Legendas Coluna TIPO

Sigl Descrição
a
SHL Shell G Coluna indicador de grupo
REP Report
FRC Forms - Commit
FRQ Forms - Query Marque * quando o módulo for um conjunto de
PLS Batch PL/SQL processos que serão detalhados abaixo.
TRN Transferencia de Arquivos (FTP) Indique no módulo os seqüenciais que compõe
LDR Sql*Loader - Carga de tabelas este módulo. Informe o tempo total.
PRC Pro*C
COB Pro*Cobol

88
OUT Outros - Especificar na descrição.

6.5- FERRAMENTA PECS DE ACOMPANHAMENTO DE TESTES.

6.5.1 - O que é o PECS

Em que se consiste uma aplicação de qualidade?

Um dos mais importantes fatores de um aplicativo é sua performance. Um aplicativo desenvolvido com o
máximo de facilidades possíveis ao usuário, cores, telas de auxílio etc.. pode se tornar inoperante caso a
sua performance não atenda os requisitos mínimos necessários para a função que foi especificado. Uma
aplicação obtêm uma qualidade superior quando alia uma alta performance com facilidades para o
usuário, chegando num ambiente de produção suficientemente testado de modo a se evitar “bugs”.

Performance Event Collection Services (PECS) é uma ferramenta disponível para o forms 4.013 e 4.5,
que permite tuning de performance através de medição de tempos de cada função de seu forms, ao
mesmo tempo analisando o quanto de seu código foi testado.

Como performance o PECS pode responder as seguintes perguntas:

· Quanto tempo demora a execução de uma trigger?


· Quantos documentos posso processar por hora?
· Quanto tempo demora para um LOV carregar 3 campos em meu forms?

Como uma ferramenta de auxílio a testes o PECS pode responder as seguintes perguntas:

· Todas as triggers de meu forms foram testadas?


· Quais telas não foram visitadas pelos meus testes?

6.5.2- Benefícios de uso do PECS

· O PECS efetua um significativo trabalho de “detetive” em seu forms durante o processo de


desenvolvimento. Usando informação que o PECS provê, você poderá entender melhor o
comportamento de seu aplicativo, localizar gargalos de performance e efetuar testes mais robustos
e executar benchmarking.

· Pode-se tambem utilizar o PECS para estudos e planejamento de capacidade das aplicações. Usando
as informações que o PECS recolhe, pode-se através das medidas obtidas efetuar testes e
simulações em análises do tipo ‘E SE ?’.

· O PECS deve ser usado juntamente com os mecanismos SQL TRACE/TKPROF.

· O PECS pode fornecer informações de performance em todos, ou em eventos selecionadas de seu


forms, assim como triggers, montagem de windows, canvas e alerts. Dentro destes eventos você
coleciona estatísticas sobre: gastos de CPU e tempo utilizado; Se o evento ocorreu ou não durante
um teste e quantas vezes a execução do evento ocorreu.

89
· O PECS armazena as informações recolhidas em formato binário comprimido gerando um arquivo
.DAT no seu disco. Ao término da análise este arquivo pode ser carregado em tabelas do database
para a geração dos relatórios de análise dos resultados obtidos.

· Além dos eventos básicos o desenvolvedor pode criar unidades lógicas, a serem analisadas pelo PECS
utilizando-se de built-ins do PECS dentro de seu código PL/SQL. Estas built-ins, permitem
informar quando o PECS deve começar a armazenar dados e quando a transação que está sendo
medida termina. Deste modo pode-se cercar uma função de seu forms transformando-a em um
evento que passa a ser analisado pelo PECS.

· PECS permite uma análise da cobertura de seus testes informando que áreas de seu forms foram
executadas e mais importante, quais áreas não foram testadas durante a fase de testes.

6.5.3- Componentes e definições do PECS.

O PECS utiliza a seguinte hierarquia de objetos:

· Occurrence

Um exemplo de um evento executado (given).

· Event
:
Uma transação com uma definição de início e fim específica de uma aplicação ou forms.

· Class

Um grupo de eventos correlacionados. Eventos pertencem a classes. Classes são “donas” de eventos.
Por exemplo: Um form é uma classe que é dona de eventos (triggers, lovs). Um WHEN-BUTTON-
PRESSED é uma ocorrência de um evento trigger.

· Run

É uma passada de teste em sua aplicação do inicio ao fim. Cada arquivo .DAT mapeia um único run
de seu experimento.

· Experiment

É um conjunto de um ou mais “run”s.

90
O sistema PECS é composto de 4 componentes:

· PECS built-ins

São built-ins que podem ser inseridas dentro de código PL/SQL, para definir eventos customizados.
Estas built-ins incluem:

¨ PECS.COLLECTOR - Inicia e termina o PECS.


¨ PECS.ADD_CLASS - Registra uma classe.
¨ PECS.ADD_EVENT - Registra um evento.
¨ PECS.ENABLE_CLASS,PECS.DISABLE_CLASS - Habilita e desabilita um classe.
¨ PECS.START_EVENT, PECS.END_EVENT, PECS.POINT_EVENT - Define instâncias do evento.

¨ PECS Assistant

Uma aplicação forms para monitorar, carregar arquivos .DAT nas tabelas PECS e visualizar os
resultados obtidos.

¨ PECS Reports

Relatórios com a análise dos dados obtidos.

¨ PECS Database Tables:

Tabelas em seu banco de dados utilizadas pelo sistema PECS, devem ser criadas dentro do usuário
SYSTEM utilizando para isso o script PECSBILD.SQL.

As seguintes tabelas são criadas:

¨ PECS_RUN
¨ PECS_EXPERIMENT
¨ PECS_CLASS
¨ PECS_CLASS_EVENTS
¨ PECS_DATA
¨ PECS_SUMMARY
.

6.5.4- Coletando informações sobre eventos do FORMS

1- Rode sua aplicação com PECS ON de modo a coletar dados, armazenando-os num arquivo .DAT

Exemplo:

f40run module=myform userid=scott/tiger pecs=yes

Este comando irá criar um arquivo de nome myform.dat.

91
2- Utilize o PECS Assistent para carregar os dados contidos no arquivo .DAT para o banco de dados.

Ou através do programa PECSLOAD via linha de comando:

PECSLOAD -f myform.dat -n exp_name scott/tiger

3- Analise os resultados via windows do PECS Assistant ou através dos reports Performance e Coverage
Reports.

Exemplo de um relatório PECS

Este procedimento carregará uma análise completa do teste executado. Caso se deseje coletar
informações apenas de determinados eventos, deve-se usar as built-ins (PECS.DISABLE) para limitar o
foco dos experimentos eliminando LOVs por exemplo.

92
6.5.5- Eventos específicos da Aplicação

Eventos da aplicação são transações lógicas definidas pelas regras de negócio de sua aplicação, vide item
1.7.

Você pode criar classes e eventos para analisar estas transações via report do PECS.

Uma classe definida C_CORRENTE pode conter, por exemplo os eventos, DEPOSITO, RETIRADA e
TRANSFERENCIA.

Para definir os eventos PECS:

· Inicie a aplicação/PECS pela linha de comando ou através da built-in PECS.COLLECTOR .

· Adicione classes e eventos via built-ins PECS.ADD_CLASS e PECS.ADD_EVENT.

· Habilite eventos e classes com a built-in PECS.ENABLE.

· Defina os eventos delimitando as ações com as built-ins PECS.START_EVENT e


PECS.END_EVENT ou marque via PECS.POINT_EVENT.

93

Você também pode gostar