Você está na página 1de 63

Boas Práticas de Programação

Tópicos que veremos

• Práticas de programação

• Performance
Práticas de programação

Veremos:

SHARE-LOCK e LOCKS de Registros

TRANSAÇÕES
LOCK de registro

O PROGRESS suporta três tipos de locks:

• NO-LOCK
• SHARE-LOCK
• EXCLUSIVE-LOCK
LOCK de registro
Se o registro está: Outros poderão:

• EXCLUSIVE-LOCK
NO-LOCK • SHARE-LOCK
• NO-LOCK

• SHARE-LOCK
SHARE-LOCK (apenas leitura)
• NO-LOCK

EXCLUSIVE-LOCK • NO-LOCK
Conceito de TRANSAÇÕES

•Uma transação é uma unidade completa de trabalho;

•Podem ser criadas automaticamente através de


comandos progress;

FOR EACH customer:


UPDATE name address address2 city state postal-code.
END.
Conceito de TRANSAÇÕES
O progress inicia uma transação quando
encontra:

• DO TRANSACTION;
• FOR EACH TRANSACTION;
• REPEAT TRANSACION;
• DO ON ENDKEY ou DO ON ERROR onde
o bloco afeta diretamente a base de dados ou
lê registros com EXCLUSIVE-LOCK;
• FOR EACH ou REPEAT que diretamente lê
registros com EXCLUSIVE-LOCK.
Conceito de TRANSAÇÕES
Observe:

REPEAT :
CREATE customer.
UPDATE custnum NAME address address2 city state
postalcode.
END.

Para cada interação do REPEAT, o bloco é uma
transação em separado, ou seja, se ocorrer algum
erro o progress irá desfazer apenas a última
passagem pelo “repeat”.
Conceito de TRANSAÇÕES
Caso você queira controlar todos os registros incluídos
neste processo você pode tornar a transação maior:

DO TRANSACTION:
REPEAT:
CREATE customer.
UPDATE custnum NAME address address2 city
state postalcode.
END.
END.
Conceito de TRANSAÇÕES

Também podemos usar o transaction para tornar


as transações menores:
Conceito de TRANSAÇÕES
DEF VAR i AS INT NO-UNDO INITIAL 1.

FIND LAST customer NO-LOCK NO-ERROR.


IF AVAIL(customer) THEN
ASSIGN i = cust-num + 1.
ELSE
RETURN ERROR.
Com isso você cria uma
DO TRANSACTION: transação menor, não
CREATE customer. bloqueando o registro por
ASSIGN customer.cust-num = i muito tempo.
customer.NAME = "teste"
customer.sales-rep = "DOS"
customer.credit-limit = 2.0000.
END.
Conceito de TRANSAÇÕES
DEF VAR i AS INT NO-UNDO INITIAL 1.

FIND LAST customer NO-LOCK NO-ERROR.


IF AVAIL(customer) THEN
ASSIGN i = cust-num + 1.
ELSE
RETURN ERROR.
Evite fazer este tipo de acesso a
CREATE customer. dados poi o mesmo torna toda a
ASSIGN customer.cust-num = i procedure uma transação.
customer.NAME = "teste"
customer.sales-rep = "DOS"
customer.credit-limit = 2.0000.
Conceito de TRANSAÇÕES

• Criação de sub-transações;

• Utilização do comando DO para


laços;
Conceito de TRANSAÇÕES

O progress inicia uma sub-transação quando


encontra um bloco definido em uma transação
ativa:

• Qualquer FOR EACH, REPEAT ou bloco de


procedure;
• Um DO ON ERROR ou DO ON ENDKEY que
contenha comandos que diretamente afete as
informações no banco de dados ou leia registros
com EXCLUSIVE LOCK.
Conceito de TRANSAÇÕES
DO TRANSACTION:
CREATE customer.
ASSIGN customer.cust-num = i Transação principal
customer.NAME = "teste"
customer.sales-rep = "DOS"
customer.credit-limit = 2.0000. Inicia uma
REPEAT:
FIND LAST order NO-LOCK NO-ERROR. sub-transação
IF NOT AVAIL(order) THEN DO:
CREATE order.
ASSIGN order.order-num = 1
order.cust-num = customer.cust-num
order.order-date = TODAY.
END. Fim da
ELSE DO: sub-transação
CREATE order.
ASSIGN order.order-num = order.order-num + 1
order.cust-num = customer.cust-num
order.order-date = TODAY. Fim transação
END. principal
END.
END.
Conceito de TRANSAÇÕES
Transações e sub-procedures:

Se uma transação é iniciada quando o PROGRESS


chama uma sub-procedure (tanto interna como
externa), a transação ficará ativa durante o escopo da
sub-procedure.
Exemplo:

Do transaction:
...
Run teste.

End.
Conceito de TRANSAÇÕES
Transações e Triggers:

• A transação começa em uma trigger, a transação


termina quando o a trigger termina.
• A transação começa em um bloco dentro de uma
trigger, a transação termina no final deste mesmo
bloco.
• Uma trigger quando executada durante uma
transação já ativa, qualquer código dentro desta
trigger que inicia uma transação, esta torna-se
uma sub-transação.
Conceito de TRANSAÇÕES

ON CHOOSE OF btupd IN FRAME fPage0 DO:


Find customer where cust-num = i-cust
exclusive-lock no-error.
IF AVAIL(customer) then
RUN addRecord .
END.
Este find Exclusive-lock torna
toda a trigger uma transação.
Conceito de TRANSAÇÕES

Do transaction:
...

APPLY “CHOOSE”:U TO BTADD IN FRAME


{&FRAME-NAME}.

...
End.
Performance

•Comandos que causam impactos;

•Má utilização de comandos existentes;

•Consumo elevado de memória;


Utilização do comando IF
repetidamente
Errado: Certo:
IF <condição 1> THEN IF <condição 1> THEN
<comando>. <comando>.
IF <condição 2> THEN ELSE
<comando>. IF <condição 2> THEN

IF <condição 3> THEN <comando>.

<comando>. ELSE...
IF <condição N> THEN
...
IF <condição N> THEN <comando>.

<comando>.
Utilização do comando IF para o uso do
método LOAD-MOUSE-POINTER
Troque:
IF CAMPO:LOAD-MOUSE-
POINTER("<DIRETÓRIO\ARQUIVO>")
IN FRAME {&FRAME-NAME} THEN.
Pode ser substituída por:
CAMPO:LOAD-MOUSE-
POINTER("<DIRETÓRIO\ARQUIVO>")
IN FRAME {&FRAME-NAME}.
Utilização do comando CASE
Trocar: Por:
IF <condição 1> THEN DO: CASE <variável e/ou campo>:
WHEN <Valor 1> THEN DO:
comandos... Comandos...
END. END.
ELSE WHEN <Valor 2> THEN DO:
IF <condição 2> THEN DO: Comando...
END.
comandos... WHEN <Valor 3> THEN DO:
Comando...
END.
END.
ELSE
END CASE.
IF <condição 3> THEN DO:
comandos...
END.
Utilização de blocos para agrupar
comandos
Trocar: Por:
IF <condição> THEN DO: IF <condição> THEN
ASSIGN VAR1 = <valor1> ASSIGN VAR1 = <valor1>
VAR2 = <valor2> VAR2 = <valor2>
VAR3 = <valor3>. VAR3 =<valor3>.
END. ELSE
ELSE DO: ASSIGN VAR1 = <valor4>
ASSIGN VAR1 = <valor 4> VAR2 = <valor5>
VAR2 = <valor 5> VAR3 = <valor6>.
VAR3 = <valor 6>.
END.
Teste de variáveis lógicas

Trocar:
IF <variável ou campo lógico> = YES THEN DO:
Comandos ...
END.
Por:
IF <variável ou campo lógico> THEN DO:
Comandos ...
END.
Utilização do comando
CAN-FIND
Trocar:
FIND <tabela> WHERE <tabela>.<campo-chave> = <VALOR> no-lock.
IF NOT AVAILABLE <tabela> THEN DO:
CREATE <tabela>.
ASSIGN <tabela>.<campo-chave>.
END.

Por:
IF NOT CAN-FIND(<tabela> WHERE <tabela>.<campo-chave> =<valor>)
THEN DO:
CREATE <tabela>.
ASSIGN <tabela>.<campo-chave>. Com o can-find você apenas faz
END. trafegar um valor lógico ao invés
do registro inteiro.
Utilização do comando DO para laços

Transação desnecessária: Correção:

... /* Início do programa */ ... /* Início do programa */

REPEAT <VAR> = 1 TO 100: DO <VAR> = 1 TO 100:

ASSIGN <variável> = <VALOR>. ASSIGN <variável> = <VALOR>.

DISPLAY ... você cria DISPLAY ...


Com o “DO”
um laço simples.
END. END.

...O/*repeat
Finalização do programa ... /* Finalização do programa */
por outro lado */
cria uma transação.
TEMP-TABLES
• Não é aconselhável a utilização de TEMP-TABLE
definida como uma variável global.
• É possível, em muitos casos, substituir uma TEMP-
TABLE global por uma TEMP-TABLE compartilhada
(NEW SHARED e SHARED) ou por passagem de
parâmetros.
• Procure defini-las como NO-UNDO.
• Crie o mínimo possível, índices.
• Procure evitar a criação de TEMP-TABLES LIKE.
• Não é necessário utilizar os comandos FOR FIRST,
FOR LAST e CAN-FIND para TEMP-TABLES.
VARIÁVEIS

• Variáveis declaradas como NO-UNDO.


• Evite as variáveis SHARED, a não ser
que a aplicação exija.
• Quando forem utilizadas variáveis do
tipo EXTENT, inicialize seu conteúdo
com apenas um comando ASSIGN.
VARIÁVEIS

Errado:
Certo:
DEF VAR c-it-codigo AS CHAR extent
DEF VAR c-it-codigo AS CHAR
10 NO-UNDO.
extent 10 NO-UNDO.
DEF VAR i-cont AS integer
ASSIGN c-it-codigo = "A".
NO-UNDO.
DO i-cont = 1 TO 10:
ASSIGN c-it-codigo[i-cont] = "A".
END.
ASSIGN
Procure agrupar ao máximo as variáveis em
um mesmo ASSIGN.
Trocar:
ASSIGN c-it-codigo = "item-1".
ASSIGN i-op-codigo = 10.
ASSIGN c-cod-roteiro = "rot-1".
Por:
ASSIGN c-it-codigo = "item-1“
i-op-codigo = 10
c-cod-roteiro = "rot-1".
BUFFER-COPY

Comando que realiza a cópia de valores entre dois


BUFFERS de registro mais rapidamente, podendo
trabalhar inclusive com TEMP-TABLES.
Exemplo:
DEF TEMP-TABLE tt-item NO-UNDO LIKE item USE-
INDEX codigo.
FOR EACH item NO-LOCK:
CREATE tt-item.
BUFFER-COPY item TO tt-item.
END.
FIELDS

Também conhecida por FIELD-LIST, permite


ao programador definir quais os campos de
determinada tabela um comando de busca irá
trazer através da rede para a máquina cliente,
reduzindo o tráfego na rede.
FOR FIRST/LAST

FOR FIRST item FIELDS (it-codigo perm-saldo-neg


pm-ja-calc) WHERE item.it-codigo = c-it-codigo
NO-LOCK:
DISP item.it-codigo.
END.
FIND CURRENT
FIND FIRST customer NO-LOCK.
REPEAT:
IF customer.cust-num = 10 THEN DO:
FIND CURRENT customer EXCLUSIVE-LOCK NO-ERROR.
ASSIGN customer.NAME = "Zezo's Sports" .
LEAVE.
END.
ELSE
FIND NEXT customer NO-LOCK.
END.
DISP customer.NAME.
FOR EACH

O FOR EACH também permite a utilização da


cláusula fields, que agiliza bastante a execução
deste comando.
FOR EACH
DEFINE BUFFER b-order FOR order.

FOR EACH customer FIELDS (cust-num) NO-LOCK,


FIRST order WHERE order.cust-num = customer.cust-num NO-
LOCK :
<comandos...>
IF order.order-date > 13/12/2001 THEN DO:
FIND b-order WHERE ROWID(b-order) = ROWID(order)
EXCLUSIVE-LOCK.
<comandos...>
END.Desta forma o registro ficará preso à partir deste ponto.
USE-INDEX

Procure não utilizar a cláusula USE-INDEX com FIND,


FOR EACH e outros comandos de busca de registros, a
menos que você realmente queira forçar a utilização de
um índice.
ROWID

As buscas utilizando o ROWID são as


mais eficientes.
OBS: Jamais grave um rowid e uma tabela para
referênciar um registro filho pois o rowid muda
após um dump/load
RUN
O comando RUN permite a execução de
PROCEDURES internas, externas e chamadas
DLL´s.
No caso de chamadas externas, dependendo do
seu tamanho, de onde ele está localizado e da
velocidade do meio de armazenamento, o tempo
de carga deste pode ser bastante longo. Por isso
as chamadas externas devem ser evitadas ao
máximo e substitui-los por PROCEDURES
INTERNAS, INCLUDES ou chamadas no modo
persistente.
PROCEDURES INTERNAS

• Podem receber um nome mais explicativo.


•Não precisam receber parâmetros e nem
variáveis SHARED.
•Permite a definição de variáveis e
BUFFERS locais.
•Cada PROCEDURE é carregada em seu
próprio segmento executável, o que permite
que cada uma delas tenha até 64KBYtes de
tamanho.
INCLUDES

As INCLUDES podem ser utilizadas para


substituir as chamadas para programas
externos e também para PROCEDURES
internas, com algumas restrições.

Não aplique lógicas muito grandes em includes, pois o


mesmo pode estourar segmento executável da
procedure que é de 64 kbytes.
INCLUDES

IF <condição> THEN DO:


{INCLUDE}
END.
ELSE DO:
{INCLUDE}
END.
PROGRAMAS PERSISTENTE

DEFINE VARIABLE h-handle AS


HANDLE NO-UNDO.

RUN cpp/cpapi001.p
PERSISTENT SET h-handle.
DELETE PROCEDURE h-handle.
WORK-FILES

Vantagens:
• É mais performático que TEMP-TABLES
• Permite busca de forma aleatória utilizando ROWID
Desvantagens:
• Não permite a utilização de índices
• Não permite a utilização de FIND, apenas FIND
NEXT/PREV/FIRST/LAST tornando o acesso sequencial
• Os dados são armazenados exclusivamente em memória
não possibilitando o armazenamento de uma grande
quantidade de registro
INDEXED-REPOSITION
ÍNDICES
ÍNDICES

• Procure utilizar EQUALITY MATCHES (“=”), na


mesma seqüência em que são definidos no índice.
Ex.: Índice código, da tabela oper-ord, do banco
MGIND: nr-ord-prod, it-codigo, cod-roteiro, op-
codigo. Procure sempre utilizar o máximo possível de
EQUALITY MATCHES, a partir do primeiro
componente do índice.
Oper-ord
nr-ord-prod
it-codigo
cod-roteiro
op-codigo
ÍNDICES

Adequado:
FOR EACH oper-ord
WHERE oper-ord.nr-ord-prod = i-nr-ord-prod
NO-LOCK:
Melhor:
FOR EACH oper-ord
WHERE oper-ord.nr-ord-prod = i-nr-ord-prod
AND oper-ord.it-codigo = c-it-codigo
Oper-ord
NO-LOCK:
nr-ord-prod
it-codigo
cod-roteiro
op-codigo
ÍNDICES

Rápido:
FOR EACH oper-ord
WHERE oper-ord.nr-ord-prod = i-nr-ord-prod
AND oper-ord.it-codigo = c-it-codigo
AND oper-ord.cod-roteiro = c-cod-roteiro
NO-LOCK:
Muito Rápido:
FOR EACH oper-ord
WHERE oper-ord.nr-ord-prod = i-nr-ord-prod Oper-ord
AND oper-ord.it-codigo = c-it-codigo
AND oper-ord.cod-roteiro = c-cod-roteiro nr-ord-prod
AND oper-ord.op-codigo = i-op-codigo it-codigo
NO-LOCK: cod-roteiro
op-codigo
ÍNDICES

Evite quebrar o índice sobre pena de ter que lêr a


base novamente para satisfazer alguma condição
do “WHERE”.

EXEMPLO:
FOR EACH customer WHERE customer.cust-num = 10 OR
customer.address <> "" NO-LOCK:
DISPLAY customer.
END.
ÍNDICES

• INEQUALITY MATCHES (“<>”)


devem ser evitados ao máximo.
• EVITE a utilização da função
MATCHES, que não faz
aproveitamento dos índices.
• A ordem dos campos na cláusula
WHERE não importa.
ÍNDICES

FOR EACH oper-ord


WHERE oper-ord.nr-ord-prod = i-nr-ord-prod
AND oper-ord.it-codigo = c-it-codigo
NO-LOCK:
Equivale a:
FOR EACH oper-ord
WHERE oper-ord.it-codigo = c-it-codico
AND oper-ord.nr-ord-prod = i-nr-ord-prod
NO-LOCK:
ÍNDICES

• Se for necessário utilizar a cláusula BY em um FOR


EACH, procure sempre informar um campo que seja
o primeiro componente de um índice.
• Evite utilizar um IF...THEN...ELSE em uma
cláusula WHERE, sob pena de obrigar o
PROGRESS a realizar uma busca seqüencial na
tabela. Utilize variáveis auxiliares para receber um
valor conforme a condição e realize a busca com
essas variáveis.
• Procure evitar ao máximo a utilização do operador
lógico OR em uma cláusula WHERE.
ÍNDICES

DEFINE VARIABLE l-teste AS LOGICAL NO-UNDO.


FOR EACH customer WHERE
customer.cust-num > 10 AND
customer.address <> (IF l-teste THEN ""
ELSE "rua seara 106")
NO-LOCK:
DISPLAY customer.
END.
Regras para seleção de índices
•1)Um índice não é utilizado (ou necessário) se for
fornecido um ROWID.
•2)Se a opção USE-INDEX é especificada, o
PROGRESS utiliza o índice informado.
•3)
Se a opção CONTAINS é usada, ele seleciona o
WORD-INDEX.
•4)Se um índice é único e todos os componentes desse
índice são comparados utilizando-se EQUALITY
MATCHES (“=”) e o outro índice é não-único, o
PROGRESS escolhe o índice único.
•5)
O índice com maior número de EQUALITY
MATCHES (“=”).
Regras para seleção de índices

•6)
O índice com uma comparação BEGINS (porque
BEGINS é considerada como 2 RANGE MATCHES)
•7)O índice com maior número de RANGE
MATCHES
•8) O índice com o critério de ordenação (BY). Isso
acontece apenas se a cláusula WHERE não prover
informações suficientes para a seleção de outro índice.
•9) Pelo nome do índice, em ordem alfabética.
•10) O índice primário.
Outras dicas de performance
Outras dicas de performance

Evite utilizar as INCLUDES de tradução (ut-liter, ut-table,


ut-field) dentro de loops.
Certo: Errado:
{utp/ut-liter.i Literal} FOR EACH item NO-LOCK:
ASSIGN c-liter = return- CREATE tt-item.
value. {utp/ut-liter.i Literal}
FOR EACH item NO-LOCK: BUFFER-COPY item TO tt-
CREATE tt-item. item
BUFFER-COPY item TO ASSIGN tt-item.msg = return-
tt-item value.
ASSIGN tt-item.msg = END.
c-liter.
END.
Outras dicas de performance
Procure tornar o programa mais “inteligente”.
Certo:
FOR EACH ord-prod
WHERE ord-prod.nr-ord-prod >= i-ord-ini
AND ord-prod.nr-ord-prod <= i-ord-fim
AND ord-prod.valorizada = no NO-LOCK:
DISP ord-prod.nr-ord-prod.
END.
Errado:
FOR EACH ord-prod
WHERE ord-prod.nr-ord-prod >= i-ord-ini
AND ord-prod.nr-ord-prod <= i-ord-fim
NO-LOCK:
IF ord-prod.valorizada THEN NEXT.
DISP ord-prod.nr-ord-prod.
END.
Outras dicas de performance

Certo:
FOR EACH movto-estoq
WHERE movto-estoq.dt-trans = DATE (c-data) NO-LOCK:
END.

Errado:
FOR EACH movto-estoq
WHERE STRING (movto-estoq.dt-trans) = c-data NO-LOCK:
END.

Isto fará com que o progress traga os registros para o


cliente para poder converter e fazer a comparação.
Outras dicas de performance

• Não utilize RAW-TRANSFER para


passagem de parâmetros.
• Use “quebra” da QUERY com SMART-
QUERIES muito complexas.
• Cuidados com LOCKS de registro para
evitar DEAD-LOCKS
PERGUNTA?