365 dias de dicas de desenvolvimento Progress OpenEdge // Dica #5

E lá vamos nós!

Quinto dia do ano, quinta dica... Sou um gênio!

Na dica #4 iniciamos o CRUD – Create, Read, Update e Delete. Hoje vamos de Read, mostrarei algumas instruções que fazem leitura de registros.

De forma resumida, temos três instruções para fazer a leitura de registros do banco de dados, são eles:

1.      Instrução FIND,

2.      Instrução FOR,

3.      Instrução Open Query.

Não comentarei sobre Dataset, pois esse ganhará uma dica para chamar de sua!

Em termos de desempenho, FIND, FOR e Open Query usam o mesmo código em linguagem C, não havendo vantagem ou justificando o uso dessa, da outra ou aquela instrução. Isso mesmo, tanto faz em termos de desempenho usar FIND, FOR ou Open Query, você fara a opção conforme a lógica necessária no teu programa.

Por favor, não se assustem com as sintaxes...

Instrução FIND

Essa instrução você deve usá-la, e somente a usará, para localizar um registro que você fará alteração.

FIND [ FIRST | LAST | NEXT | PREV ] record

     [constant ]

     [ OF table ]

     [ WHERE expression ]

     [ USE-INDEX index ]

     [ USING [ FRAME frame ] field

      [ AND [ FRAME frame ] field ] ...

    ]

     [ SHARE-LOCK | EXCLUSIVE-LOCK | NO-LOCK ]

     [ NO-WAIT ]

     [ NO-PREFETCH ]

     [ NO-ERROR ]

O que são esses três pontos “...” (conhecidas como Elipses*)?

Para as opções de instruções Progress ABL e OpenEdge – sim, comandos do banco de dados também possuem opções com três pontos “...” – esses três pontos “...” significam repetição e você pode usar um ou várias vezes essa opção. Infelizmente, foi justamente uma opção que não se usa mais... Sim, esse USING não se usa mais.

*eu não conhecia as elipses por esse nome, chamava de três pontos, Burro!

Continuando... agora vai!

Vou até adicionar destaque...

Nunca! Em hipótese alguma você usará o FIND para buscar o valor de um campo, seja nome de cliente, o valor de um pedido, a descrição da natureza de operação, a data da nota fiscal! Nunca mesmo, e se eu descobrir, ficarei de mal contigo!

E se você usa o FIND para buscar alguns míseros campos, já lhe aviso, está ERRADO!

Por quê?

Porque o FIND trará TODOS os campos da tabela, TODOS, sim, TODOS, nem mais, nem menos, TODOS os campos da tabela!

E mais...

Não há como usar o FIND para buscar somente um campo da tabela.

No meu arquivo de ajuda da Progress – Ei Progress, eu gosto de compartilhar e escrever, me contrata! :D – Eu teria removido algumas opções antigas ou dado mais destaque, explicação para elas.

A minha sintaxe da instrução FIND ficaria assim...

FIND [ FIRST | LAST | NEXT | PREV ] record

     [ OF table ]

     [ WHERE expression ]

     [ USE-INDEX index ]

     [ EXCLUSIVE-LOCK | NO-LOCK ]

     [ NO-WAIT ]

     [ NO-PREFETCH ]

     [ NO-ERROR ]

Sem share-lock!

Share-lock não é do bem!

É o tal de lock compartilhado que pode fazer teu programa entrar num dead-lock, é mais ou menos assim que ocorre. Um usuário Alice acessa o cliente José da Silva – o programa não tem o tipo de lock e como tem alteração, a ABL (em toda a sua benevolência) usa o share-lock -, mas no meio da alteração do registro a natureza a chama, ou aquele café DILIÇA acabou de ficar pronto, e Alice não termina a alteração e sai da estação; como desastre de avião não é somente por uma causa, então temos o usuário Bob, que localiza o mesmo cliente José da Silva que Alice iniciou a alteração, e ele também inicia a alteração, mas Bob tenta salvar a alteração, nesse momento o pobre do banco de dados tem a transação da Alice iniciada e pendente e tem a transação do Bob pedindo para ser efetivada, e agora?

Agora aconteceu o desastre de avião...

No alt text provided for this image

Bob tem essa mensagem, informando que Alice estava fazendo alteração...


No alt text provided for this image

E Alice, após retornar do café e tentar salvar, verá essa tela...


Um usuário bloqueando a alteração do outro!

Infelizmente isso ocorre pelo uso do share-lock e pela transação não ter sido efetivada, mas principalmente, se tivesse usado exclusive-lock, o dead-lock não teria ocorrido!

Usando o exclusive-lock somente Bob teria a janela de informação para quando o registro estivesse em alteração por outro usuário, e essa janela ela até tem nome – pelo menos eu a batizei – é a janela de Wait (aguardo), e se você notar, há uma opção no-wait no find para essa janela não ser exibida, mas isso é outra história – ou outra dica.

A instrução FIND, quando mal-usada também pode transformar todo um programa e suas cinco mil linhas em uma única transação – sim! Temos esse tipo de programa por aí, sem modularização e com desempenho terrível.

Mais uma dica anotada aqui na lista de dicas futuras.

A instrução FIND tem outra variação, mas essa é somente para mudar o estado do lock do registro quando já localizamos um registro.

FIND CURRENT record

     [ SHARE-LOCK | EXCLUSIVE-LOCK | NO-LOCK ]

     [ NO-WAIT ]

     [ NO-ERROR ]

Mais uma anotação para dicas futuras!

Fica tranquilo, estou anotando as dicas futuras (já tenho outras cinco, não, são seis dicas vindouras somente com o que falamos até aqui!

Instrução FOR

A instrução FOR é bastante complexa, como a sintaxe abaixo nos mostra, e ela serve para que o banco de dados envie ou os registros que atendem a cláusula Where – se existir uma – ou todos os registros da tabela, permitindo a alteração de um registro por vez.

A sua sintaxe é...

label: ]

FOR [ EACH | FIRST | LAST ]record-phrase

  [ , [ EACH | FIRST | LAST ]record-phrase ]...

  [query-tuning-phrase ]

  [ BREAK ]

  [ BY expression[ DESCENDING ]

  | COLLATE ( string , strength[ , collation] ) [ DESCENDING ]

  ]...

  [variable = expression1 TO expression2[ BY k ]]

  [ WHILE expression ]

  [ TRANSACTION ]

  [ STOP-AFTER expression ]

  [on-error-phrase ]

  [on-endkey-phrase ]

  [on-quit-phrase ]

  [on-stop-phrase ]

  [frame-phrase ] :

for-body

A instrução FOR é bacaninha demais da conta!

Ela pode localizar vários registros, permitindo que nosso programa exiba, altere ou elimine esses registros, pode organizar esses registros de uma forma toda especial, mas a pobre da instrução pode ser muito mal escrita, e não é difícil de encontrar programas demoradíssimos – algumas horas ou até dias – os tais For Each For Ever assim nomeados pelo Sandro C.

Além disso, a instrução FOR se beneficia de vários mecanismos bacanas do Progress OpenEdge, muitos deles dimensionados por parâmetros do banco de dados como o -Mm, -prefetchDelay, -prefetchNumRecs etc.

O FOR FIRST com uso da opção FIELDS esse sim você deve usar para buscar nome de cliente, total do pedido, descrição da natureza de operação de um ou alguns campos...

Para buscar SOMENTE o campo nome do cliente (customer.name)... preste atenção nos destaques!

For first customer

   Fields(customer.name)

   No-lock

Where customer.custnum = 3005:

End.

If available customer then

   Display customer.name.

Para buscar os campos nome (customer.name), endereço (customer.address) e limite de crédito (customer.creditlimit) usaremos...

For first customer

   Fields( customer.name

           customer.address

           customer.creditlimit)

   No-lock

Where customer.custnum = 3005:

End.

If available customer then

   Display

       customer.name

       customer.address

       customer.creditlimit.

A instrução FOR possui uma imensidão de opções e não conseguirei falar todas ou as mais importante delas nessas dicas. Escutei um “Ah, que pena!”?

Instrução Open Query

E para completar a dica, temos a instrução Open Query com essa sintaxe bonitona aqui... 

OPEN QUERY query { FOR | PRESELECT } EACH record-phrase

  [ , { EACH | FIRST | LAST } record-phrase]...

  [ query-tuning-phrase ]

  [ BREAK ]

  [ BY expression [ DESCENDING ]

    | COLLATE ( string , strength [ , collation ] ) [ DESCENDING ]

  ] ...

  [ INDEXED-REPOSITION ]

  [ MAX-ROWS num-results]

A Open Query é tão bacana, mas tão bacana, mas tão bacana que ela pode ser definida ou não - ao contrário da instrução FIND e FOR que você só coloca no programa - a instrução Open Query e sua definição pode determinar quais campos trafegarão da tabela do banco de dados para a sua AVM. Você pode também permitir o acesso aleatório dos registros – enquanto o FOR EACH não, permitindo somente no modo sequencial -, e ainda, a Open Query permite termos vários registros em memória e para navegarmos por esses registros usamos a instrução GET.

A instrução Get tem essa sintaxe...

GET { FIRST | NEXT | PREV | LAST | CURRENT } query

   [ SHARE-LOCK | EXCLUSIVE-LOCK | NO-LOCK ]

   [ NO-WAIT ]

E para definir uma Query você usa o Define Query...  

DEFINE {[[ NEW ] SHARED ]|[ PRIVATE | PROTECTED ][ STATIC ]}

  QUERY query

  FOR buffer-name[field-list][ , buffer-name[field-list]]...

  [ CACHE n]

  [ SCROLLING ]

  [ RCODE-INFORMATION ]

 A primeira consulta - query, sem definição e trazendo todos os campos do banco...

open query qcustomer

   for each customer

   no-lock

where customer.name begins 'ja'. 

get first qcustomer.

repeat while query-off-end("qcustomer") = false:

   display

       customer.custnum

       customer.name

       customer.address.

   get next qcustomer.

end.

Nessa query – sem ser definida e sem usarmos a opção fields na definição - o resulta é esse...

No alt text provided for this image

Todos os campos vieram do banco de dados, mesmo exibindo somente três na instrução Display!




Agora farei uma consulta definida – define query -, trazendo do banco somente os campos que pretendemos exibir – fields, e com navegação aleatória - scrolling.

define query qcustomer

   for customer

   fields( customer.custnum

           customer.name

           customer.address)

   scrolling.

 open query qcustomer

   for each customer

   no-lock

where customer.name begins 'ja'. 

get first qcustomer.

repeat while query-off-end("qcustomer") = false:

    display

       customer.custnum

       customer.name

       customer.address.

   get next qcustomer.

end.

No alt text provided for this image

Se usarmos o debugger poderemos ver os benefícios de definir a query e de termos informado quais campos pretendemos buscar do banco...

Isso mesmo! Somente três campos vieram do banco de dados.

Compare as duas queries, a que não foi definida com a que foi definida com o define query. Mesmo que na primeira query mostramos somente três campos, TODOS os demais campos da tabela são trazidos do banco – falta da opção fields faz isso! Já na query que usamos fields na definição, somente três campos vieram do banco – economia, a gente vê por aqui!

E aí, gostou? Achou algum absurdo acima? Deixe seus comentários, críticas e sugestões são bem-vindas...

Abraço e até amanhã!

Boa tarde, Jailson! Espero que esteja tudo bem, você anda meio sumido as dicas pararm no dia 06, a proposito muito obrigado pela iniciativa. Eu não entendi quando você disse que o USING não é mais utilizado, estou iniciando e vejo ele pra tudo quanto é lado. O que seria usado no lugar?

Cleberson Silva

Especialista Dados na BRF | SQL | Power BI | DAX | Engenharia de Dados | Databricks

2 a

Excelente dica Jaison Antoniazzi,e parabéns pela iniciativa...

Carlos Henrique Soares Pires

Desenvolvedor Progress | ASP.Net | Xamarin Mobile App | Analista de Sistemas

2 a

Ótima Dica!

Manoel Alves

Desenvolvedor Full Stack | Especialista em Vue.js, Quasar Framework, Node.js e Sequelize | Foco em Arquitetura Escalável e Experiência do Usuário

2 a
Alvarino José de Almeida

ESPECIALISTA TI III ( DATASUL e PO-UI)

2 a

Muito boa dica!!

Entre para ver ou adicionar um comentário

Outras pessoas também visualizaram

Conferir tópicos