Postagem em destaque

BlackTDN :: cArqTab :: Otimizando a Abertura do Sistema: Como a Escolha do Método de Carregamento Afeta o Desempenho

_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

Postagens mais visitadas