domingo, 1 de julho de 2018

Search VS Search ALL

Olá!

No COBOL temos algumas formas para percorrer uma lista/tabela.

Sim!

Além do bom e simples PERFORM UNTIL utilizado para a realização de loops, temos as opções: SEARCH ou SEARCH ALL. Que dependendo do processamento a ser realizado podem apresentar melhor performance no tratamento dos dados.

Por exemplo, pense na situação que você possui uma lista com milhares de registros, e será necessário verificar se há um registro com um valor específico. Ou seja, terá que realizar no mínimo uma iteração na lista e uma expressão condicional em cada registro iterado.

No primeiro momento logo se pensa em fazer um PERFORM UNTIL e dentro dele utilizar um IF para verificar cada registro. Não é? Sim. Entretanto, por outro lado, podemos fazer uma solução mais elegante e mais performática com o uso do SEARCH ou SEARCH ALL.

Antes de conhecer a sintaxe dos comandos SEARCH e SEARCH ALL, vamos ter um duelo para visualizar as características de cada um. Vamos lá!

FELIPE BARBOSA FERREIRA Características do comando Search VS Search ALL
Características do comando Search VS Search ALL

Cuidados importantes ao utilizar o SEARCH ALL
  • Se a tabela estiver preenchida parcialmente e ordenada de forma ascendente, inicializar os campos não populados com HIGH-VALUES
  • Se a tabela estiver preenchida parcialmente e ordenada de forma descendente, inicializar os campos não populados com LOW-VALUES
  • Na declaração da tabela adicionar ASCENDING ou DESCENDING KEY IS

Declaração da tabela:
     *
     * -- Tabela ALUNO: Matricula - Nome - Idade
      01 TABELA-ALUNO.
          03 TAB-ALU OCCURS  0 TO 500 TIMES
                     DEPENDING ON GDA-QT-ALU
                 ASCENDING KEY IS TAB-ALU-MTC
                       INDEXED BY NDX-TAB-ALU.
              05 TAB-ALU-MTC          PIC  9(004).
              05 TAB-ALU-NM          PIC  X(050).
              05 TAB-ALU-IDD          PIC  9(004).
     *

Os comandos:
  • SEARCH
     * -- Realiza a pesquisa na tabela
          SEARCH TAB-ALU VARYING PSC-ALUNO
     *        Quando não encontrado
              AT END
                  MOVE 'Aluno não encontrado' TO GDA-NM-ALU
     *
     *        Quando encontrado
              WHEN TAB-ALU-MTC(NDX-TAB-ALU) EQUAL MATRICULA-ALU
                  MOVE TAB-ALU-NM(NDX-TAB-ALU) TO GDA-NM-ALU
     *
          END-SEARCH.
     *

  • SEARCH ALL
     * -- Realiza a pesquisa na tabela
          SEARCH ALL TAB-ALU
     *        Quando não encontrado
              AT END
                  MOVE 'Aluno não encontrado' TO GDA-NM-ALU
     *
     *        Quando encontrado
              WHEN TAB-ALU-MTC(NDX-TAB-ALU) EQUAL MATRICULA-ALU
                  MOVE TAB-ALU-NM(NDX-TAB-ALU) TO GDA-NM-ALU
     *
          END-SEARCH.
     *

Visualmente são os mesmos comandos, pois neste exemplo somente o uso do ALL é a diferença entre os comandos. 

Mas lembre-se das características de cada um, pois a maior diferença está no processamento dos dados e não na escrita do comando. Também, na se esqueça da devida atenção ao se utilizar o SEARCH ALL, tanto na lista a ser processada como na declaração da tabela.

Consultar a relação de Termos e Abreviaturas acesse aqui.

FELIPE BARBOSA FERREIRA
Até o próximo post!

sábado, 13 de maio de 2017

Commit encerra uso do Cursor

Olá!

Ao ser necessário realizar a persistência de vários registros no banco de dados (DB) é uma boa prática realizar Commit (salvar alterações que são feitas no DB) após uma quantidade registros processados.

Pois, com essa prática caso ocorra algum erro durante a execução do programa é possível realizar o restart da aplicação a partir do último registro processado, assim ganhando processamento pelo fato de não ter que processar todos os registros novamente.

Exemplo com o processamento de um arquivo:
Ler o arquivo com as informações dos clientes e atualizar o DB com esses dados. Considerando que o arquivo tenha 1 milhão de registros, o Commit deve ser feito a cada 1.000 registros atualizados.

Para esse exemplo o fluxo de processamento é bem simples, sendo criado uma estrutura de repetição para ler o arquivo de entrada e atualizar o DB, e a cada 1.000 registros atualizados executar o Commit para salvar os dados dos cliente já atualizados.

Nada de mais, né? Então, vamos para outro exemplo.

Exemplo de processamento para vários registros de uma tabela - cursor:
Realizar a leitura de todos os saldos da tabela de cliente (cliente.saldos), e adicionar R$ 100 de limite de crédito (cliente.limite_credito) onde o saldo do cliente for maior que R$ 500. De modo que a cada 1.000 clientes atualizados realizar Commit no DB.

Nesse exemplo o fluxo de processamento é semelhante ao do primeiro exemplo, pois também é necessário uma estrutura de repetição para ler o saldo de cada cliente armazenado na tabela, e se esse saldo for superior a R$ 500 adicionar R$ 100 ao limite de crédito do cliente, e a cada 1.000 clientes atualizados realizar Commit para salvar os clientes já atualizados.

Entretanto, se criado o seguinte fluxo:
...
     Abrir cursor
     Ler cursor
     Loop até fim de cursor
         Se saldo cliente > 500 adicionar 100 ao limite de crédito
         Se quantidade de clientes atualizados > que 1.000 fazer Commit
         Ler cursor para ver saldo do próximo cliente
...

Será reportado erro ao tentar ler o saldo do próximo cliente após ter executado a instrução Commit dos clientes já atualizados.

Pois, ao executar o comando Commit o cursor é automaticamente fechado. Assim, quando o programa tentar ler o saldo do próximo cliente será retornado erro pela tentativa de ler o cursor fechado.

Umas das possíveis soluções é: abrir o cursor, realizar a leitura de todos os clientes e armazenar em memória (tabela interna Cobol) aqueles que satisfação a condição definida, fechar o cursor. Agora, com os clientes que estão em memória fazer a persistência dos novos dados na tabela e a cada 1.000 atualizações fazer Commit.

Note que essa é somente uma das várias soluções, pois dependendo da quantidade de registros não será possível armazenar eles em memoria, de modo a ter que usar outra forma de processamento para realizar a aplicação.

Enfim, tenha em mente que após a execução da instrução Commit o cursor que esteja aberto é automaticamente encerrado, desse modo impossibilitando a leitura do próximo registro.


Consultar a relação de Termos e Abreviaturas acesse aqui.

FELIPE BARBOSA FERREIRA
Até o próximo post!

domingo, 23 de abril de 2017

Mostrar o valor do índex de uma tabela

Olá!

Uma das estruturas básicas para a declaração de uma tabela no COBOL, é:

           03 NOME-TABELA OCCURS  0 TO QUANTIDADE-REGISTROS TIMES
                          DEPENDING ON QUANTIDADE-REGISTROS-INSERIDOS.
               05 VAR-1                 PICTURE
               05 VAR-2                 PICTURE
               05 VAR-n                 PICTURE

Entretanto, dependendo da aplicação a ser realizada, pode ser necessário trabalhar com o índex da tabela, desde modo a declaração da tabela deve ser feita conforme:

           03 NOME-TABELA OCCURS  0 TO QUANTIDADE-REGISTROS TIMES
                      DEPENDING ON QUANTIDADE-REGISTROS-INSERIDOS
                        INDEXED BY NOME-INDEX.
               05 VAR-1                 PICTURE
               05 VAR-2                 PICTURE
               05 VAR-n                 PICTURE

O detalhe é que esse índex é para utilização de processamento lógico, o que quero dizer é: a variável índex da tabela, não é uma variável comum e requer um tratamento específico. Pois, por exemplo, os comandos: 

FELIPE BARBOSA FERREIRA - Erro ao usar o index da tabela em comandos
Erro com a variável índex da tabela

Retornam erro relativo ao uso ilegal dessa variável.

  • Note que se utilizado diretamente no comando Display é retornado erro. Então como mostrar o valor do índex da tabela?

Para exibir o valor dessa variável é necessário criar uma auxiliar numérica PIC 9(n) e atribuir o valor do índex para ela. E para realizar a movimentação do valor, deve ser usado o comando SET, veja:

           SET VAR-AUXILIAR TO NOME-INDEX.

Exemplo prático:
O comando Search é utilizado para realizar a pesquisa de um registro em uma tabela, e para usar esse comando é necessário realizar a declaração da tabela com o índex.

No programa de exemplo foi criado uma tabela para armazenar os alunos ganhadores de uma competição, sendo que a ordem de colocação do aluno é definida pela ordem de cadastrado na tabela, ou seja, o primeiro registro é equivalente ao ganhador do primeiro lugar da competição. Assim, com o uso do Search é possível apresentar a posição de um determinado aluno.

Então, quando o Search encontrar o aluno, o valor contido no índex da tabela é referente à posição do registro.
Veja abaixo as declarações básicas e comandos principais para o código de exemplo:
  • Declaração das variáveis:
      *
      * -- Quantidade de alunos
       77 GDA-QT-ALU                    PIC  9(004).
      *
      * -- Index para loop
       77 NDX                           PIC  9(001) VALUE ZEROS.
      *
      * -- Para ser usado no SEARCH
       77 MATRICULA-ALU                 PIC  9(004) VALUE ZEROS.
      *
      * -- Armazenar a posicao do aluno na premiacao da competicao
       77 POSICAO-PREMIACAO             PIC  9(004) VALUE ZEROS.
      *
      *----------------------------------------------------------------*
      * -- Tabela para armazenar os alunos ganhadores
      *----------------------------------------------------------------*
      *
      * -- Tabela ALUNO: Matricula - Nome - Idade
       01 TABELA-ALUNO-PREMIADOS.
           03 TAB-ALU OCCURS  0 TO 5 TIMES
                      DEPENDING ON GDA-QT-ALU
                        INDEXED BY NDX-TAB-ALU.
               05 TAB-ALU-MTC           PIC  9(004).
               05 TAB-ALU-NM            PIC  X(050).
               05 TAB-ALU-IDD           PIC  9(004).
      *
  • Comando Search
      * -- Realiza a pesquisa na tabela
           SEARCH TAB-ALU
      *        Quando nao encontrado
               AT END
                   SET POSICAO-PREMIACAO TO 9999
      *
      *        Quando encontrado
               WHEN TAB-ALU-MTC(NDX-TAB-ALU) EQUAL MATRICULA-ALU
                   SET POSICAO-PREMIACAO TO NDX-TAB-ALU
      *
           END-SEARCH.
      *
Note que quando o aluno é encontrado o valor do índex da tabela (NDX-TAB-ALU) é movimentado para a variável (POSICAO-PREMIACAO) por meio do comando SET, está variável é usada de forma auxiliar para exibir o valor contido no índex da tabela.
  • Mostrar o valor do índex da tabela, e demais dados do aluno
      * -- Mostrar os dados do aluno premiado
      *
           IF POSICAO-PREMIACAO EQUAL 9999
               DISPLAY 'Não foi premiado o aluno com matrícula: ' 
                        MATRICULA-ALU
           ELSE
               DISPLAY 'Posição ' POSICAO-PREMIACAO ' - '
                        TAB-ALU-NM(POSICAO-PREMIACAO)
           END-IF.

Resposta da execução do programa para o exemplo:
FELIPE BARBOSA FERREIRA - Resposta programa exemplo mostrar valor do index da tabela
Resposta da execução do código de exemplo







a
Enfim, quando a aplicação requer a utilização do índex da tabela, tenha cuidado e atenção com os comandos que serão utilizados para manipular o valor dessa variável.

Para Ctrl + C do código acesse aqui.
Consultar a relação de Termos e Abreviaturas acesse aqui.
FELIPE BARBOSA FERREIRA

Até o próximo post!