_Créditos da imagem: Gerada com auxílio do ChatGPT_
# cArqTab :: Como o carregamento de informações em uma variável pode tornar lenta a abertura do sistema TOTVS Microsiga Protheus
---
# Comparando LoadFromDBQuery e LoadFromDBWhile: Vantagens e Desempenho
No desenvolvimento de soluções para o TOTVS Microsiga Protheus, otimizar o tempo de execução das funções é essencial, especialmente quando lidamos com grandes volumes de dados. Neste artigo, vamos analisar a diferença de desempenho entre os métodos `LoadFromDBQuery` e `LoadFromDBWhile`, utilizando os tempos de execução para demonstrar a vantagem de cada abordagem.
## Contexto do Teste
Para avaliar o desempenho, executamos dois procedimentos distintos:
- **U_TSTArqTabLoadDBQuery**: utiliza a função `LoadFromDBQuery`.
- **U_TSTArqTabLoadDBWhile**: utiliza a função `LoadFromDBWhile`.
Os comandos foram executados no ambiente Cygwin, e PowerShell e os tempos foram medidos da seguinte forma:
*Cygwin
```bash
$ time C:/totvs/tst/smartclient/smartclient.exe -q -p=U_TSTArqTabLoadDBQuery -a=01:01 -c=LOCALHOST -e=USERFUNCTION -m -l
real 0m7,253s
user 0m0,000s
sys 0m0,015s
$ time C:/totvs/tst/smartclient/smartclient.exe -q -p=U_TSTArqTabLoadDBWhile -a=01:01 -c=LOCALHOST -e=USERFUNCTION -m -l
real 0m35,420s
user 0m0,015s
sys 0m0,015s
```
### Diferença de Tempo
- **`LoadFromDBQuery`**: Tempo de execução total: aproximadamente 7 segundos.
- **`LoadFromDBWhile`**: Tempo de execução total: aproximadamente 35 segundos.
*PowerShell
```bash
Measure-Command { Start-Process -FilePath "C:\totvs\tst\smartclient\smartclient.exe" -ArgumentList "-q", "-p=U_TSTArqTabLoadDBQuery", "-a=01:01", "-c=LOCALHOST", "-e=USERFUNCTION", "-m", "-l" -Wait }
Days : 0
Hours : 0
Minutes : 0
Seconds : 7
Milliseconds : 91
Ticks : 70919283
TotalDays : 8,20825034722222E-05
TotalHours : 0,00196998008333333
TotalMinutes : 0,118198805
TotalSeconds : 7,0919283
TotalMilliseconds : 7091,9283
Measure-Command { Start-Process -FilePath "C:\totvs\tst\smartclient\smartclient.exe" -ArgumentList "-q", "-p=U_TSTArqTabLoadDBWhile", "-a=01:01", "-c=LOCALHOST", "-e=USERFUNCTION", "-m", "-l" -Wait }
Days : 0
Hours : 0
Minutes : 0
Seconds : 18
Milliseconds : 33
Ticks : 180330167
TotalDays : 0,000208715471064815
TotalHours : 0,00500917130555556
TotalMinutes : 0,300550278333333
TotalSeconds : 18,0330167
TotalMilliseconds : 18033,0167
```
### Diferença de Tempo
- **`LoadFromDBQuery`**: Tempo de execução total: aproximadamente 8 segundos.
- **`LoadFromDBWhile`**: Tempo de execução total: aproximadamente 18 segundos.
A diferença é significativa: `LoadFromDBQuery` é cerca de 2 a 5 vezes mais rápido do que `LoadFromDBWhile`.
## Análise das Funções
### LoadFromDBQuery
A função `LoadFromDBQuery` executa uma consulta diretamente no banco de dados utilizando SQL. Esse método aproveita os recursos de agregação de strings e de filtros diretamente no banco de dados, o que reduz a necessidade de processar dados linha a linha no lado do cliente.
#### Vantagens:
- **Desempenho**: Executa a lógica de forma mais eficiente diretamente no banco de dados, reduzindo o número de interações entre o cliente e o servidor.
- **Menor Uso de Recursos**: A agregação de resultados no banco de dados minimiza a carga de processamento do lado cliente.
- **Facilidade de Manutenção**: O código SQL pode ser adaptado conforme a necessidade, facilitando ajustes de performance e leitura de dados.
### LoadFromDBWhile
A função `LoadFromDBWhile` faz a leitura dos registros utilizando um loop `while` para percorrer cada linha da tabela `SX2` e montar a string `cArqTab`.
#### Desvantagens:
- **Desempenho Inferior**: O loop `while` processa os registros linha por linha, o que é significativamente mais lento, especialmente em tabelas grandes.
- **Maior Consumo de Recursos**: Por ser um processo iterativo e dependente do lado do cliente, consome mais tempo de CPU e memória.
- **Manutenção**: Códigos iterativos tendem a ser mais complexos e difíceis de ajustar para otimização.
## Conclusão
O uso de `LoadFromDBQuery` traz uma vantagem clara em termos de desempenho para cenários onde a leitura e a agregação de dados podem ser realizadas diretamente no banco de dados. A execução é mais rápida, eficiente e consome menos recursos do lado cliente, sendo ideal para ambientes que necessitam de alta performance e grande volume de dados.
Por outro lado, o `LoadFromDBWhile` pode ser considerado em situações onde a lógica de agregação é muito específica e difícil de implementar diretamente em SQL, mas, para a maioria dos casos, a abordagem com `LoadFromDBQuery` será mais vantajosa.
O impacto da escolha entre essas duas abordagens é visível na diferença de tempo apresentada: **7 segundos contra 35 segundos** em um exemplo simples. Para sistemas em produção, essa diferença pode representar um ganho de performance significativo, melhorando a experiência do usuário final e a eficiência operacional.
---
*TLPP
```xBase
#include "totvs.ch"
#include "dbinfo.ch"
#include "tbiconn.ch"
#include "parmtype.ch"
#define __EMPFIL__ "01:01"
/*
Powershell:
Measure-Command { Start-Process -FilePath "C:\totvs\tst\smartclient\smartclient.exe" -ArgumentList "-q", "-p=U_TSTArqTabLoadDBQuery", "-a=01:01", "-c=LOCALHOST", "-e=USERFUNCTION", "-m", "-l" -Wait }
Measure-Command { Start-Process -FilePath "C:\totvs\tst\smartclient\smartclient.exe" -ArgumentList "-q", "-p=U_TSTArqTabLoadDBWhile", "-a=01:01", "-c=LOCALHOST", "-e=USERFUNCTION", "-m", "-l" -Wait }
CYGWin:
time C:/totvs/tst/smartclient/smartclient.exe -q -p=U_TSTArqTabLoadDBQuery -a=01:01 -c=LOCALHOST -e=USERFUNCTION -m -l
time C:/totvs/tst/smartclient/smartclient.exe -q -p=U_TSTArqTabLoadDBWhile -a=01:01 -c=LOCALHOST -e=USERFUNCTION -m -l
*/
procedure U_TSTArqTabLoadDBQuery(cEmpFil)
local aEmpFil as array
local cEmp,cFil as character
local cArqTab:="" as character
DEFAULT cEmpFil:=__EMPFIL__
aEmpFil:=StrTokArr(cEmpFil,":")
cEmp:=aEmpFil[1]
cFil:=aEmpFil[2]
PREPARE ENVIRONMENT EMPRESA (cEmp) FILIAL (cFil)
cArqTab:=TSTArqTabLoad():LoadFromDBQuery(cArqTab)
if (FWMakeDir("c:\tmp\"))
MemoWrite("c:\tmp\"+ProcName()+".log",cArqTab)
endif
RESET ENVIRONMENT
FWFreeArray(@aEmpFil)
return
procedure U_TSTArqTabLoadDBWhile(cEmpFil)
local aEmpFil as array
local cEmp,cFil as character
local cArqTab:="" as character
DEFAULT cEmpFil:=__EMPFIL__
aEmpFil:=StrTokArr(cEmpFil,":")
cEmp:=aEmpFil[1]
cFil:=aEmpFil[2]
PREPARE ENVIRONMENT EMPRESA (cEmp) FILIAL (cFil)
cArqTab:=TSTArqTabLoad():LoadFromDBWhile(cArqTab)
if (FWMakeDir("c:\tmp\"))
MemoWrite("c:\tmp\"+ProcName()+".log",cArqTab)
endif
RESET ENVIRONMENT
FWFreeArray(@aEmpFil)
return
class TSTArqTabLoad
static method LoadFromDBQuery(cArqTab as character) as character
static method LoadFromDBWhile(cArqTab as character) as character
end class
static method LoadFromDBQuery(cArqTab) class TSTArqTabLoad
local cTCGetDB:=Upper(TCGetDB()) as character
local cSX2AliasTmp:=getNextAlias() as character
local cSX2TableName as character
paramtype 1 var cArqTab as character optional default ""
cSX2TableName:=SX2->(dbInfo(DBI_FULLPATH))
cSX2TableName:="%"+cSX2TableName+"%"
if ("MSSQL"$cTCGetDB)
beginSQL alias cSX2AliasTmp
SELECT STRING_AGG(CAST(CONCAT(SX2.X2_CHAVE,SX2.X2_MODO,'/') AS VARCHAR(MAX)),'') AS X2_MODO
FROM %exp:cSX2TableName% SX2
WHERE SX2.%notDel%
AND SX2.X2_CHAVE<>''
endSQL
cArqTab:=strTran((cSX2AliasTmp)->X2_MODO," ","")
(cSX2AliasTmp)->(dbCloseArea())
dbSelectArea("SX2")
elseif ("POSTGRES"$cTCGetDB)
beginSQL alias cSX2AliasTmp
SELECT STRING_AGG(CONCAT(SX2.X2_CHAVE,SX2.X2_MODO,'/'),'') AS X2_MODO
FROM %exp:cSX2TableName% SX2
WHERE SX2.%notDel%
AND SX2.X2_CHAVE<>''
endSQL
cArqTab:=strTran((cSX2AliasTmp)->X2_MODO," ","")
(cSX2AliasTmp)->(dbCloseArea())
dbSelectArea("SX2")
elseif ("ORACLE"$cTCGetDB)
beginSQL alias cSX2AliasTmp
SELECT LISTAGG(SX2.X2_CHAVE||SX2.X2_MODO||'/','') WITHIN GROUP (ORDER BY SX2.X2_CHAVE) ON OVERFLOW TRUNCATE '.../' AS X2_MODO
FROM %exp:cSX2TableName% SX2
WHERE SX2.%notDel%
AND SX2.X2_CHAVE<>''
endSQL
cArqTab:=strTran((cSX2AliasTmp)->X2_MODO," ","")
(cSX2AliasTmp)->(dbCloseArea())
dbSelectArea("SX2")
endif
if (empty(cArqTab).or.(".../"$cArqTab))
cArqTab:=TSTArqTabLoad():LoadFromDBWhile(cArqTab)
endif
return(cArqTab)
static method LoadFromDBWhile(cArqTab) class TSTArqTabLoad
local aAreaSX2:=SX2->(FWGetArea()) as array
paramtype 1 var cArqTab as character optional default ""
if (empty(cArqTab).or.(".../"$cArqTab))
SX2->(dbGotop())
while (SX2->(!eof()))
cX2Modo:=(SX2->(X2_CHAVE+X2_MODO))
if (!(cX2Modo$cArqTab))
cArqTab+=cX2Modo+"/"
endif
SX2->(dbSkip())
end while
endif
FWRestArea(@aAreaSX2)
FWFreeArray(@aAreaSX2)
return(cArqTab)
```
---
Comentários
Postar um comentário