Usina BRAdvPL Guia
Entrar
← Todos os tópicos
FUNDAMENTOPublicado

Desenvolvendo queries no Protheus

cQuery → ChangeQuery() → TCQUERY / Embedded SQL | FWPreparedStatement / FWExecStatement → WorkArea

Estruture consultas SQL portáveis e eficientes no Protheus e use statements parametrizados com FWPreparedStatement e FWExecStatement quando os valores precisarem ser separados da instrução SQL.

SQLDBAccessTopConnQueryPerformanceBanco de dadosFWExecStatementFWPreparedStatementSetInSQL parametrizado
01 · VISÃO GERAL

Visão geral

O DBAccess continua sendo a camada de acesso aos bancos SQL homologados, e recursos como RetSqlName(), GetNextAlias(), ChangeQuery(), TCQUERY e Embedded SQL permanecem importantes. Para consultas em que os valores variam, FWPreparedStatement organiza parâmetros da instrução e FWExecStatement permite executar a consulta e abrir o resultado em um alias. A documentação oficial também registra que SetIn(), herdado de FWPreparedStatement por FWExecStatement, atende parâmetros usados em cláusulas SQL IN. A escolha deve considerar portabilidade, filtros de filial e exclusão lógica, tipos retornados, fechamento do alias e liberação do objeto.

02 · SINTAXE

Sintaxe

cQuery → ChangeQuery() → TCQUERY / Embedded SQL | FWPreparedStatement / FWExecStatement → WorkArea

Parâmetros

GetNextAlias()
FunçãoOpcional

Gera um alias temporário para o result set e evita colisões com áreas já abertas.

RetSqlName()
FunçãoOpcional

Converte o alias lógico do Protheus no nome físico da tabela no banco de dados.

ChangeQuery()
FunçãoOpcional

Adapta a instrução SQL para compatibilidade com os bancos homologados.

TcSetField()
FunçãoOpcional

Ajusta campos não caractere do resultado para os tipos AdvPL esperados.

SqlOrder()
FunçãoOpcional

Converte uma expressão de índice AdvPL para uso em uma cláusula ORDER BY.

xFilial()
FunçãoOpcional

Retorna a filial adequada para o filtro da tabela informada.

DToS()
FunçãoOpcional

Converte uma data para AAAAMMDD quando esse formato for necessário na construção da consulta.

Métodos

FWPreparedStatementBase para preparar uma instrução SQL com parâmetros separados dos valores.FWPreparedStatement():New( <cQuery> )
FWExecStatementStatement voltado à execução da consulta parametrizada e abertura do resultado em alias.FWExecStatement():New( <cQuery> )
SetIn()Preenche um parâmetro associado a uma cláusula SQL IN. Em FWExecStatement, o método é herdado de FWPreparedStatement.oStatement:SetIn( <nParameter>, <aValues> )
03 · EXEMPLO PRÁTICO

Query básica portável

#Include "TOTVS.ch"
#Include "TopConn.ch"

User Function ExQueryBasica()
    Local cAlias := GetNextAlias()
    Local cQuery := ""

    // RetSqlName() resolve o nome físico da tabela no banco de dados.
    cQuery := "SELECT A1_COD, A1_NOME FROM " + RetSqlName("SA1")
    cQuery += " WHERE A1_FILIAL = '" + xFilial("SA1") + "'"
    cQuery += " AND D_E_L_E_T_ = ' '"

    // ChangeQuery() adapta a instrução para os bancos homologados.
    cQuery := ChangeQuery(cQuery)

    TCQuery cQuery New Alias (cAlias)

    While !(cAlias)->(Eof())
        // A1_NOME é o campo de nome do cliente.
        ConOut((cAlias)->A1_COD + " - " + (cAlias)->A1_NOME)
        (cAlias)->(DbSkip())
    EndDo

    // Sempre feche a WorkArea aberta para o result set.
    (cAlias)->(DbCloseArea())
Return
Resultado esperado

A consulta usa nome físico, filial, exclusão lógica, alias dinâmico e fechamento explícito.

04 · EXEMPLO PRÁTICO

Query de Recno para posicionar a tabela real

User Function ExQueryRecno()
    Local cAlias := GetNextAlias()
    Local cQuery := ""

    // R_E_C_N_O_ identifica o registro físico correspondente ao resultado.
    cQuery := "SELECT R_E_C_N_O_ RECNO, A1_COD, A1_NOME FROM " + RetSqlName("SA1")
    cQuery += " WHERE A1_FILIAL = '" + xFilial("SA1") + "'"
    cQuery += " AND D_E_L_E_T_ = ' '"
    cQuery := ChangeQuery(cQuery)

    TCQuery cQuery New Alias (cAlias)

    While !(cAlias)->(Eof())
        DbSelectArea("SA1")
        SA1->(DbGoTo((cAlias)->RECNO))
        // Depois do DbGoTo(), SA1 está posicionada no registro real.
        ConOut(SA1->A1_COD + " - " + SA1->A1_NOME)
        (cAlias)->(DbSkip())
    EndDo

    (cAlias)->(DbCloseArea())
Return
Resultado esperado

O result set identifica os registros e DbGoTo() posiciona a tabela real usando R_E_C_N_O_.

05 · EXEMPLO PRÁTICO

Query parametrizada com FWExecStatement

User Function ExFWExec()
    Local cQuery := ""
    Local cAlias := ""
    Local oStmt

    // O marcador ? mantém o valor fora do texto SQL.
    cQuery := "SELECT A1_COD, A1_NOME FROM " + RetSqlName("SA1")
    cQuery += " WHERE A1_FILIAL = ? AND D_E_L_E_T_ = ' '"
    cQuery := ChangeQuery(cQuery)

    oStmt := FWExecStatement():New(cQuery)
    oStmt:SetString(1, xFilial("SA1"))
    cAlias := oStmt:OpenAlias()

    While !(cAlias)->(Eof())
        // A1_NOME é o campo de nome do cliente.
        ConOut((cAlias)->A1_COD + " - " + (cAlias)->A1_NOME)
        (cAlias)->(DbSkip())
    EndDo

    (cAlias)->(DbCloseArea())
    oStmt:Destroy()
Return
Resultado esperado

A consulta parametriza a filial, abre o resultado em alias e libera os recursos explicitamente.

06 · EXEMPLO PRÁTICO

Cláusula IN com SetIn()

User Function ExSetIn()
    Local cQuery := ""
    Local cAlias := ""
    Local aClientes := {"000010", "000020", "000030"}
    Local oStmt

    // O segundo marcador representa a lista usada pela cláusula IN.
    cQuery := "SELECT A1_COD, A1_NOME FROM " + RetSqlName("SA1")
    cQuery += " WHERE A1_FILIAL = ? AND A1_COD IN (?) AND D_E_L_E_T_ = ' '"
    cQuery := ChangeQuery(cQuery)

    oStmt := FWExecStatement():New(cQuery)
    oStmt:SetString(1, xFilial("SA1"))
    oStmt:SetIn(2, aClientes)
    cAlias := oStmt:OpenAlias()

    While !(cAlias)->(Eof())
        ConOut((cAlias)->A1_COD + " - " + (cAlias)->A1_NOME)
        (cAlias)->(DbSkip())
    EndDo

    (cAlias)->(DbCloseArea())
    oStmt:Destroy()
Return
Resultado esperado

SetIn() associa um array ao marcador da cláusula IN.

BOAS PRÁTICAS
  • Selecione apenas as colunas necessárias; evite SELECT * para reduzir tráfego e acoplamento.
  • Use GetNextAlias() em vez de um alias fixo.
  • Obtenha os nomes físicos com RetSqlName() e passe a consulta por ChangeQuery().
  • Filtre cada campo de filial com xFilial() da própria tabela; não compare indiscriminadamente filiais de tabelas diferentes.
  • Exclua registros apagados logicamente com D_E_L_E_T_ = espaço em cada tabela participante.
  • Prefira JOIN em sintaxe ANSI e aplique os filtros da tabela associada no local coerente com o tipo de JOIN.
  • Use agregações como COUNT, SUM, MAX e MIN para reduzir linhas retornadas quando o resultado desejado for consolidado.
  • Feche obrigatoriamente a WorkArea com DbCloseArea() ao terminar.
  • O desempenho de uma query depende do conjunto formado pelo código AdvPL, DBAccess, SGBD, infraestrutura e volume de dados. Meça consultas lentas e consumo de recursos antes de alterar índices ou ampliar hardware.
  • Planos de execução, índices, estatísticas, fragmentação e Page Split ajudam a explicar gargalos no SQL Server. Essas análises pertencem à administração do banco e devem ser feitas com evidências do ambiente.
  • Índices personalizados precisam ser documentados e novamente verificados após atualizações do Protheus, pois mudanças estruturais podem removê-los ou torná-los inadequados.
  • Arquivamento e expurgo podem reduzir o volume operacional, mas dependem de regras de negócio, obrigações legais, política de retenção, cópia de segurança e possibilidade de recuperação.
  • A compressão de dados pode reduzir armazenamento e leitura de páginas, porém consome CPU e depende dos recursos da versão e edição do SQL Server. Avalie por teste e monitore o resultado.
  • A tabela física pode variar conforme o grupo de empresas e o compartilhamento configurado. Por isso, use RetSqlName() para obter seu nome e xFilial() para aplicar o contexto correto, sem deduzir sufixos manualmente.
  • A estrutura persistida é governada pelo dicionário de dados do Protheus. Campos, tabelas e índices criados diretamente no SGBD podem causar divergências em atualizações ou na abertura de rotinas; alterações estruturais devem seguir os mecanismos suportados pela TOTVS.
  • Campos de controle como R_E_C_N_O_ e D_E_L_E_T_ fazem parte da persistência administrada pelo DBAccess. Consultas precisam respeitar a exclusão lógica, mas não devem assumir que toda característica física histórica continua idêntica em qualquer release.
  • Em estruturas tradicionais, datas do dicionário são frequentemente armazenadas como caracteres AAAAMMDD e valores vazios podem ser representados por espaços ou zero em vez de NULL. Confirme o tipo real e converta o result set com TcSetField() quando necessário.
  • Índices personalizados devem considerar todos os jogos físicos de tabelas aplicáveis ao ambiente, ser documentados e alinhados com o dicionário e com a equipe responsável pelas atualizações.
  • Prefira parametrização quando valores variáveis puderem ser separados da estrutura SQL.
  • Continue usando RetSqlName(), xFilial() e o tratamento de D_E_L_E_T_ conforme a tabela e a finalidade da consulta.
  • Feche o alias retornado por OpenAlias() e destrua o statement quando terminar o processamento.
ARMADILHAS COMUNS
  • R_E_C_N_O_, D_E_L_E_T_ e R_E_C_D_E_L_ são campos de controle do DBAccess; não trate exclusão lógica como exclusão física.
  • Um result set de query normalmente avança para frente; não presuma a mesma navegação de uma tabela ISAM.
  • SQL específico de SQL Server, Oracle ou outro SGBD pode falhar nos demais bancos homologados.
  • Em funções de agregação, campos não agregados precisam ser compatíveis com a cláusula GROUP BY.
  • A query de Recno é útil quando é necessário posicionar a tabela real para alterar ou excluir, mas adiciona uma etapa e não deve ser usada sem necessidade.
  • Não concatene entrada não confiável diretamente no SQL. Valide rigorosamente os valores e use mecanismos seguros disponíveis.
  • Não desabilite nem remova índices padrão do Protheus sem orientação formal da TOTVS, análise de um DBA, homologação e plano de reversão.
  • Não aplique FILLFACTOR, reconstrução de índices ou alterações de estatísticas de forma global. Uma configuração útil para uma tabela pode prejudicar outra.
  • As recomendações do artigo são baseadas em experiência prática com SQL Server e em um contexto histórico do Protheus; não são documentação oficial da TOTVS nem se transferem automaticamente para outros bancos ou versões.
  • Não derive o nome físico de uma tabela pela empresa ou filial. Regras de compartilhamento mudam a estrutura disponível; RetSqlName() e xFilial() evitam pressupostos frágeis.
  • Não crie campos, tabelas, constraints ou índices diretamente no banco sem avaliar o dicionário, o configurador, a compatibilidade com o DBAccess e o processo oficial de atualização.
  • Particionamento, compressão, replicação, cluster e recursos In-Memory dependem do SGBD, da versão do Protheus/DBAccess e da homologação vigente. As limitações descritas em 2012 não devem ser tratadas como estado atual do produto.
  • Não trate FWExecStatement como substituto automático para qualquer query: escolha a técnica conforme o contexto, portabilidade e necessidade de parametrização.
  • A posição informada nos métodos SetString(), SetIn() e equivalentes deve corresponder à ordem dos marcadores da instrução.
  • Evite concatenar diretamente valores externos ou variáveis de usuário ao texto SQL quando a parametrização puder ser utilizada.

Conteúdos relacionados

REFERÊNCIAS
  1. TOTVS. Desenvolvendo queries no Protheus. TDN. Criado por Sergio Luis de Alcantara Silveira; última alteração indicada em 17 fev. 2017.
  2. TOTVS. Embedded SQL. TDN; última alteração indicada em 17 set. 2024.
  3. TOTVS. Comando TCQUERY. TDN.
  4. LIMA, Fabrício. 5 motivos para quem utiliza o Protheus (TOTVS) contratar um DBA SQL Server. Blog, publicado em 14 dez. 2013, com atualizações posteriores. Referência complementar de experiência prática; não é documentação oficial da TOTVS.
  5. INOWE, Marcel. Dicas sobre o banco de dados do Protheus (TOTVS). 4SQLServer, 12 set. 2012. Referência histórica e prática sobre SQL Server; os comentários da própria publicação registram correções e mudanças de versões posteriores.
  6. MICROSIGA. Programação ADVPL — Utilização de Query. Documentação oficial histórica Programação ADVPL X SQL, 27 ago. 2006. Seção "Particularidades Protheus".
  7. MICROSIGA. Manual de Programação. Documento colaborativo, arquivo datado de 11 jul. 2001. Referência histórica.
  8. TOTVS. FWExecStatement. TDN.
  9. TOTVS. FWPreparedStatement. TDN.
  10. TOTVS. Cross Segmentos — ADVPL — FwExecStatement — SetIn. Central de Atendimento.
Situação
Publicado
Página criada em
Última revisão em
Idioma original
Português
Revisão
Usina.BR
0 comentário(s) aprovado(s)

Comentários

Ainda não há comentários aprovados.

Entre com Google ou Microsoft para comentar.

Powered by Usina Docs · Alpha