Pular para o conteúdo principal

Postagem em destaque

BlackTDN :: Como Criar Relatórios de Cotações com Dados Agrupados Usando SQL

img: DALL·E 2024-08-09 07.00.00 - A high-quality image showcasing a detailed SQL query being written in a code editor, such as VS Code, on a dark theme background. ... ## Como Criar Relatórios de Cotações com Dados Agrupados Usando SQL Quando trabalhamos com sistemas ERP e precisamos gerar relatórios de cotações que apresentam dados de múltiplos fornecedores, é comum encontrarmos a necessidade de pivotar registros que, originalmente, são apresentados de forma vertical. Isso é especialmente útil quando o objetivo é comparar preços ou condições de diferentes fornecedores para um mesmo produto em uma única linha do relatório. Neste artigo, vamos explorar uma abordagem para transformar registros verticais em colunas, facilitando a impressão de relatórios que consolidam informações de vários fornecedores em uma única linha. Vamos utilizar SQL com técnicas de pivotagem, e ao final, mostraremos como estender essa técnica para um número variável de fornecedores. ### Estrutura do Relatór

Protheus :: Otimizando o Relatório FINR550 ( Razonete de Contas Correntes )

Mais uma vez, e a pedido de minha querida amiga Carla Soneta, tive a missão de tentar otimizar o programa FINR550 que gera o relatório Razonete de Contas Correntes. A missão pareceu-me um desafio e aceitei (principalmente por não ser a minha área de negócios). Para que isso fosse possível primeiro eu precisaria do original do FINR550.prx mais atual (uma coisa boa, a Totvs/Microsiga fornece os fontes dos relatórios). Com o FINR550.prx em mãos começou a análise para verificar como otimizá-lo sem alterar sua lógica, estrutura e regras. Pude identificar, Considerando que o cliente que solicitou a otimização utiliza o SGBD DB2 e que possui a "Localização" apenas para o País Brasil, os seguintes pontos:
  1. Chamadas de funções desnecesárias;
  2. Resolver xFilial();
  3. Prefixar os campos e funções de db com seus respectivos Alias;
  4. Resolver "Localização";
  5. Resolver compilação condicional;
  6. Retirar parte do código específica para CodeBase;
  7. Armazenar retorno de funções em variáveis locais e reaproveitá-las quando necessário;
  8. Substituir a chamada a MsSeek() por dbSeek(); e
  9. Retirar a parte de relatório personalizável (tReport).

Vamos ao que interessa:

1. Chamadas de funções desnecesárias
Notei que o código original possuia várias chamadas a dbSelectArea(), xFilial(), dbSetOrder(), etc dentro do laço While/End While. Sendo assim elas foram retiradas. E tratamento de campos e funções de db efetuados conforme item 3.

Notei que chamavam Abs() várias vezes para um mesmo valor, então o número de chamadas a Abs() foi reduzido armazenando o conteúdo em uma variável conforme ítem 7.

Pude verificar que xFilial() era usada dentro de Laço. Então a chamada foi retirada do laço e xFilial() foi resolvida conforme ítem 2.


2. Resolver xFilial()
xFilial() é uma função em Advpl() para retornar a filial correte para uma determinada tabela conforme modo de acesso definido no dicionário de Tabelas ( o SX2). Se a Tabela estiver compartilhada entre filiais xFilial() retornará brancos, caso contrário, retornará a Filial armazenada na variável de ambiente setada com o conteúdo da filial correte. É uma péssima prática resolver xFilial() dentro de um laço, seja ele qual for ( While, For, etc.). Então para cada alias em que xFilial() precisava ser resolvido criei uma variável para esse fim e usei variável no lugar de xFilial como em:

Local cSA1Filial := xFilial("SA1")
Local cSA2Filial := xFilial("SA2")
Local cSE1Filial := xFilial("SE1")
Local cSE2Filial := xFilial("SE2")
Local cSE5Filial := xFilial("SE5")
Local cSX5Filial := xFilial("SX5")

e Depois usando:

While SE1->( !Eof() )

If mv_par18 == 1 // Seleciona clientes por conta contabil
SA1->( dbSeek(cSA1Filial+SE1->(E1_CLIENTE+E1_LOJA) ) )
If SA1->( A1_CONTA <> mv_par20 )
SE1->( dbSkip() )
Loop
Endif
Endif

ao ivés de

If mv_par18 == 1 .And. !(TcGetDb() $ "MSSQL/MSSQL7/ORACLE") // Seleciona clientes por conta contabil
dbSelectArea("SA1")
MsSeek(xFilial()+SE1->E1_CLIENTE+SE1->E1_LOJA)
If SA1->A1_CONTA <>A1_CONTA > mv_par20
dbSelectArea("SE1")
dbSkip()
Loop
Endif
Endif

3. Prefixar os campos e funções de db com seus respectivos Alias

Para evitar chamadas desnecessárias à função dbSelectArea() prefixei todos os campos com seus respectivos Alias.

Onde estava, por exemplo:

dbSelectArea("SE1")

IF E1_TIPO $ MVRECANT

dbSkip()

ENDIF

Alterei para

IF SE1->E1_TIPO $ MVRECANT

SE1->( dbSkip() )

EndIF

Onde estava:

dbSelectArea("SE1")

dbSetOrder(1)

Alterei para:

SE1->( dbSetOrder(1) )

Onde estava:

dbSelectArea("SE1")

...

dbSkip()

Alterei para:

SE1->( dbSkip() )

e assim por diante.


4. Resolver "Localização"

Para que testar se o País Local é o Brasil se sei que o código só será utilizado aqui. Então para deixa-lo mais enxuto e evitar testes desnecessários, retirei toda parte do código que não se referiam ao cPaisLoc == "BRA".

5. Resolver compilação condicional

Sabendo que o SGBD a ser utilizado seria o DB2, toda parte do código específica para CodeBase e/ou AS400() ou outro SGBD foi retirada.

6. Retirar parte do código específica para CodeBase

Idem ao item 5 e considerando que a parte retirada estava diretamente relacionada ao tratamento já dado à "View" retornada pela "Query".

7. Armazenar retorno de funções em variáveis locais e reaproveitá-las quando necessário
Tinha algo como:

nSaldoAtu -= ABS(cNomeArq->VALOR)
nTotDeb += ABS(cNomeArq->VALOR)
nTotDebG += ABS(cNomeArq->VALOR)
nSalAtuG -= ABS(cNomeArq->VALOR)

Oberve que ABS(cNomeArq->VALOR) era chamada 4 vezes para retornar o valor absoluto de um mesmo campo e armazena-la em variáveis diferentes. Imagine isso dentro de um Laço executado 1000 vezes. ABS seria executada 4000 vezes desnecessáriamente. Para solucionar o problema fiz o seguinte:

nABSValor := Abs(cNomeArq->VALOR)
nSaldoAtu -= nABSValor
nTotDeb += nABSValor
nTotDebG += nABSValor
nSalAtuG -= nABSValor
Observe que agora Abs(), se o laço for executado 1000 vezes só será executado 1000 vezes.

8. Substituir a chamada a MsSeek() por dbSeek(); e

MsSeek() é uma função "bufferizada" criada em Advpl para evitar o reposicionamento do "Ponteiro" do Banco de Dados quando este já estiver posicionado na chave que se deseja procurar. É útil, por exemplo, se usou a função de pesquisa dbSeek() para pesquisar e depois quer saber se o registro está posicionado evitando ter que pesquisa-lo novamente. Ai sim MsSeek() é útil, caso contrário é um desperdício, uma vez que MsSeek() faz vários tratamentos para verificar se a chave atual é a que procura e, se não for, irá chamar dbSeek() para posicionar.

9. Retirar a parte de relatório personalizável (tReport).

Considerando que o cliente não utiliza o FINR55o personalizável, a parte do código foi retirada para facilitar a manutenção do código.

Bem, é isso o que tinha a dizer sobre otimização do códgo FINR550 (baseado no código padrão). Para consultar na íntegra as alterações feitas no código e poder comparar com o original da microsiga utilizado como base para a otimização clique aqui

Utilize o apdiff ou outra ferramenta para comparar os códigos e visualizar as alterações.

Obs.: Sempre que for otimizar um código do padrão obtenha a última versão disponível junto a Totvs/Microsiga. Lembrando que a Totvs/Microsigadisponibiliza os fontes de Relatórios.

[]s

иαldσ

Comentários

  1. Boa noite Naldo.

    Muito bom, parabéns.

    Att.,

    Talvane (Arapiraca/AL)

    ResponderExcluir
  2. Bom dia Naldo, teria como voce me enviar o fonte do FINR550 otimizado??

    ResponderExcluir
  3. jfernandocv@gmail.com ou jfcvilela@sistemafieg.org.br

    ResponderExcluir
  4. Boa tarde,

    Teria como me enviar o fonte
    cleberfiscal23@gmail.com

    ResponderExcluir

Postar um comentário

Postagens mais visitadas