
**DNATech :: Processos set-based em TOPCONN no Protheus**
Na postagem anterior falei sobre um ponto importante:
Muitas rotinas Protheus não precisam de mais threads.
Precisam de menos loops.
O problema clássico aparece quando uma rotina começa a crescer e vira uma sequência de:
```advpl
While !EOF()
DbSeek(...)
RecLock(...)
Campo := Valor
MsUnlock()
DbSkip()
EndDo
```
Ou:
```advpl
While !EOF()
cQuery := MontaSQL(...)
DbUseArea(.T., "TOPCONN", TCGenQry(,,cQuery), cAlias)
DbSkip()
EndDo
```
Esse padrão aumenta roundtrips, pressão no DBAccess, locks, tempo de execução e complexidade.
A refatoração mais eficiente normalmente não é paralelizar o problema.
É mudar o paradigma.
Entram aqui os processos **set-based em TOPCONN**.
Em vez de processar registro por registro, o Protheus passa a orquestrar operações em lote:
```tlpp
cQuery := "INSERT INTO " + cTmp + " (R_E_C_N_O_, CODIGO, TOTAL) "
cQuery += "SELECT "
cQuery += DNATechSQLProcessTools():RecNoSql(cTmp, "BASE.CODIGO") + ", "
cQuery += "BASE.CODIGO, "
cQuery += "SUM(BASE.VALOR) "
cQuery += "FROM " + RetSQLName("SD2") + " BASE "
cQuery += "WHERE BASE.D_E_L_E_T_ = ' ' "
cQuery += "GROUP BY BASE.CODIGO"
DNATechSQLProcessTools():Exec(cQuery, "CargaResumo", cTmp)
```
A ideia é centralizar um conjunto de helpers para rotinas massivas:
```tlpp
DNATechSQLProcessTools():TmpTable()
DNATechSQLProcessTools():DropTable(cTmp)
DNATechSQLProcessTools():Refresh(cTmp)
DNATechSQLProcessTools():Exec(cQuery, cEtapa, cTmp)
DNATechSQLProcessTools():RecNoSql(cTmp, cOrder)
DNATechSQLProcessTools():SqlStr(cValor)
DNATechSQLProcessTools():NumSql(nValor)
DNATechSQLProcessTools():DateSql(dData)
DNATechSQLProcessTools():ArrayIn(aDados, 1, .T.)
```
Isso reduz repetição e evita erros comuns em SQL dinâmico.
Um exemplo importante é o `R_E_C_N_O_`.
Ao fazer `INSERT INTO ... SELECT` direto em uma temporária TOPCONN, não dá para simplesmente ignorar o `R_E_C_N_O_`.
Uma forma segura é gerar em lote:
```sql
ISNULL((SELECT MAX(R_E_C_N_O_) FROM TMP),0)
+ ROW_NUMBER() OVER (ORDER BY CAMPO1, CAMPO2)
```
Encapsulado:
```tlpp
static method RecNoSql(cTable, cOrder) class DNATechSQLProcessTools
return "ISNULL((SELECT MAX(R_E_C_N_O_) FROM " + cTable + "),0) " + ;
"+ ROW_NUMBER() OVER (ORDER BY " + cOrder + ")"
```
Outro ponto útil é transformar arrays pequenos em filtros SQL.
Para poucos registros:
```tlpp
aVendedores := { {"000001"}, {"000002"}, {"000003"} }
cVendIn := DNATechSQLProcessTools():ArrayIn(aVendedores, 1, .T.)
cQuery += " AND SA3.A3_COD IN (" + cVendIn + ") "
```
Para caches pequenos, o array funciona muito bem.
Para volume maior, o melhor caminho é criar uma tabela real ou temporária TOPCONN, carregar os dados uma vez e fazer `JOIN`.
Regra prática:
```text
Poucos registros -> array/cache em memória
Muitos registros -> tabela real ou temporária TOPCONN
Processamento -> SQL set-based
```
Também dá para padronizar conversões numéricas, datas e strings:
```tlpp
cQuery += "WHERE D2_EMISSAO BETWEEN "
cQuery += DNATechSQLProcessTools():DateSql(dDataDe)
cQuery += " AND "
cQuery += DNATechSQLProcessTools():DateSql(dDataAte)
```
E evitar conversão insegura de campos numéricos armazenados como caractere:
```tlpp
cDesc := DNATechSQLProcessTools():NumericSql("SZ3.Z3_FX01")
```
Por baixo:
```sql
ISNULL(
TRY_CONVERT(FLOAT,
NULLIF(REPLACE(RTRIM(CONVERT(VARCHAR(40), CAMPO)), ',', '.'), '')
),
0)
```
Esse tipo de helper parece pequeno, mas em rotinas grandes faz muita diferença.
Ele evita que cada rotina tenha sua própria versão de:
- montagem de `IN`;
- tratamento de aspas;
- conversão de data;
- `ISNULL`;
- geração de `R_E_C_N_O_`;
- criação e limpeza de temporárias;
- execução SQL com log;
- `TCRefresh`.
No final, o ganho não vem apenas de SQL.
Vem de arquitetura.
O Protheus continua fazendo o que ele faz bem: contexto, regra, tela, orquestração.
O banco passa a fazer o que ele faz melhor: processar conjuntos.
Antes:
```text
Loop
Seek
Calcula
Grava
Próximo
```
Depois:
```text
Cria temporária
Insere em lote
Calcula em lote
Atualiza em lote
Consolida resultado
Limpa temporárias
```
Menos thread.
Menos roundtrip.
Menos lock.
Menos timeout.
Mais previsibilidade.
A pergunta deixa de ser:
“Quantas threads preciso para essa rotina aguentar?”
E passa a ser:
“Quantos loops consigo eliminar dessa rotina?”
Essa mudança de mentalidade é onde a performance real começa.
---
`#DNATech` `#Protheus` `#TOTVS` `#AdvPL` `#TLPP` `#TOPCONN` `#DBAccess` `#SQLServer` `#Performance` `#ERP` `#Refactoring` `#ArquiteturaDeSoftware`
---
Referência de estilo: [Quando múltiplas threads mascaram problemas de arquitetura: uma refatoração real no TOTVS Protheus]( https://www.blacktdn.com.br/2026/05/dnatech-quando-multiplas-threads.html)
Comentários
Postar um comentário