Introdução

Uma classe relativamente recente e que vejo poucos programadores usarem é a FwTemporaryTable. Ela serve para criar e manipular tabelas temporárias no banco de dados. Uma classe cheia de recursos e não é muito difícil de se utilizar.

Fique ligado!

Uma observação importante é que seu uso é obrigatório para criação de tabelas temporárias caso queira migrar o dicionário do ERP para o banco de dados. Sabendo disso, o uso das funções CriaTrab, DbCreate, MsCreate, Copy To e etc. para criação de tabelas temporárias no banco de dados se tornou proibido. Caso haja alguma customização com essas funções, elas serão detectadas como bug crítico pela ferramenta TOTVS CodeAnalysis e impedirão sua migração de dicionário de dados, sendo necessária a substituição pela classe FwTemporaryTable.

A descrição do erro é: O uso de drive ISAM na linha Microsiga Protheus foi descontinuado.

Bora Codar !!

Bom, sem mais delongas, vamos analisar um exemplo de como criar uma tabela usando essa classe:

// Instancio o objeto
oTable  := FwTemporaryTable():New('TRB')

aCpoData := {}

// Crio com array com os campos da tabela
aAdd(aCpoData, {'TMP_FILIAL', TamSx3('E1_FILIAL')[3]    , TamSx3('E1_FILIAL')[1]    , 0})
aAdd(aCpoData, {'TMP_CLIENT', TamSx3('E1_CLIENTE')[3]   , TamSx3('E1_CLIENTE')[1]   , 0})
aAdd(aCpoData, {'TMP_LOJA'  , TamSx3('E1_LOJA')[3]      , TamSx3('E1_LOJA')[1]      , 0})
aAdd(aCpoData, {'TMP_PREFIX', TamSx3('E1_PREFIXO')[3]   , TamSx3('E1_PREFIXO')[1]   , 0})
aAdd(aCpoData, {'TMP_NUM'   , TamSx3('E1_NUM')[3]       , TamSx3('E1_NUM')[1]       , 0})
aAdd(aCpoData, {'TMP_PARCEL', TamSx3('E1_PARCELA')[3]   , TamSx3('E1_PARCELA')[1]   , 0})
aAdd(aCpoData, {'TMP_VALOR' , TamSx3('E1_VALOR')[3]     , TamSx3('E1_VALOR')[1]     , TamSx3('E1_VALOR')[2]})
aAdd(aCpoData, {'TMP_VALLIQ', TamSx3('E1_VALLIQ')[3]    , TamSx3('E1_VALLIQ')[1]    , TamSx3('E1_VALLIQ')[2]})
aAdd(aCpoData, {'TMP_SALDO' , TamSx3('E1_SALDO')[3]     , TamSx3('E1_SALDO')[1]     , TamSx3('E1_SALDO')[2]})

// Adiciono os campos na tabela
oTable:SetFields(aCpoData)

// Adiciono os índices da tabela
oTable:AddIndex('01', {'TMP_FILIAL','TMP_PREFIX','TMP_NUM','TMP_PARCEL'})
oTable:AddIndex('02', {'TMP_FILIAL','TMP_CLIENT','TMP_LOJA'})

// Crio a tabela no banco de dados
oTable:Create()

Ao instanciar a classe, você pode ou não informar o Alias da tabela no método New. No meu caso, informei o Alias TRB. Caso não seja informado, o sistema irá preencher com o próximo Alias disponível.

Caso queira posicionar em um registro dentro do programa, você pode utilizar os seguintes comandos:

// Caso você saiba o Alias
TRB->TMP_NUM

// Caso você não saiba o Alias – primeira opção
cTable := oTable:GetAlias()
(cTable)->TMP_NUM

// Caso você não saiba o Alias – segunda opção
(oTable:GetAlias())->TMP_NUM

SetFields

O método SetFields recebe os campos para criação da tabela, ele deve receber um array onde suas posições devem ser preenchidas da seguinte maneira:

  1. Nome do campo
  2. Tipo do campo (Caractere, numérico, etc.)
  3. Tamanho do campo
  4. Decimal do campo

AddIndex

O método AddIndex é opcional, e você pode estar utilizando para criar índices em sua tabela temporária. Informe o nome do índice no primeiro parâmetro e um array com os campos daquele índice no segundo parâmetro.

Create

Por fim, o método Create efetiva a criação da tabela no banco de dados.

Ótimo! Agora que a tabela está criada, como podemos fazer para popular? Existem duas maneiras bem simples.

A primeira e mais usada é a seguinte:

// Faço uma consulta SQL dos dados que desejo popular
    BeginSql Alias _cAlias
    
        %NoParser%
        SELECT
            SE1.E1_FILIAL,
            SE1.E1_CLIENTE,
            SE1.E1_LOJA,
            SE1.E1_PREFIXO,
            SE1.E1_NUM,
            SE1.E1_PARCELA,
            SE1.E1_VALOR,
            SE1.E1_VALLIQ,
            SE1.E1_SALDO
        FROM %TABLE:SE1% (NOLOCK) SE1
        WHERE
            SE1.E1_SALDO > 0 AND
            DATEDIFF(DAY,SE1.E1_VENCREA,CONVERT(DATE,GETDATE())) > 15 AND
            SE1.%NOTDEL%
            
    EndSQL

    (_cAlias)->(DbGoTop())
    
    DbSelectArea('TRB')

    // Insiro todos os dados na minha tabela
    While(!(_cAlias)->(EoF()))
     
        RecLock('TRB', .T.)

            TRB->TMP_FILIAL := (_cAlias)->E1_FILIAL
            TRB->TMP_CLIENT := (_cAlias)->E1_CLIENTE
            TRB->TMP_LOJA   := (_cAlias)->E1_LOJA
            TRB->TMP_PREFIX := (_cAlias)->E1_PREFIXO
            TRB->TMP_NUM    := (_cAlias)->E1_NUM
            TRB->TMP_PARCEL := (_cAlias)->E1_PARCELA
            TRB->TMP_VALOR  := Round((_cAlias)->E1_VALOR,2)
            TRB->TMP_VALLIQ := Round((_cAlias)->E1_VALLIQ,2)
            TRB->TMP_SALDO  := Round((_cAlias)->E1_SALDO,2)

        TRB->(MsUnlock())
        
        (_cAlias)->(DbSkip())
        
    EndDo
    
    TRB->(DbGoTop())
    
    (_cAlias)->(DbCloseArea())

Importante! Caso utilize essa forma de preenchimento e não tenha informado um alias para a tabela, lembre-se de substituir o alias TRB pelo método GetAlias.

Existe uma outra forma de popular esses dados e que é mais performático que o anterior, inserindo diretamente via comando SQL:

    cFields := ''

    // Busco todos os campos da tabela temporária e preencho numa variável
    For nI := 1 To Len(aCpoData)
    
        cFields += aCpoData[nI,1] + ','
    
    Next nI
    
    cFields := Left(cFields, Len(cFields) -1)
    
    // Monto o comando SQL
    cQuery := "INSERT INTO " + oTable:GetRealName()
    cQuery += " (" + cFields + ") "
    cQuery += " SELECT "
    cQuery += "     SE1.E1_FILIAL, "
    cQuery += "     SE1.E1_CLIENTE, "
    cQuery += "     SE1.E1_LOJA, "
    cQuery += "     SE1.E1_PREFIXO, "
    cQuery += "     SE1.E1_NUM, "
    cQuery += "     SE1.E1_PARCELA, "
    cQuery += "     SE1.E1_VALOR, "
    cQuery += "     SE1.E1_VALLIQ, "
    cQuery += "     SE1.E1_SALDO "
    cQuery += " FROM " + RetSqlName('SE1') + " (NOLOCK) SE1 "
    cQuery += " WHERE "
    cQuery += "     SE1.E1_SALDO > 0 AND "
    cQuery += "     DATEDIFF(DAY,SE1.E1_VENCREA,CONVERT(DATE,GETDATE())) > 10 AND "
    cQuery += "     SE1.D_E_L_E_T_ = '' "

    // Executo o comando SQL
    If(TcSqlExec(cQuery) < 0 .and. !Empty(TcSqlError()))
    
        MsgAlert('Ocorreu um erro ao executar o comando SQL!' + CRLF + CRLF + TcSqlError(), 'Erro ao popular tabela')
        
    Endif

O conteúdo da tabela será preenchido bem mais rápido do que da forma anterior!

GetRealName

Aqui utilizamos o método GetRealName, esse método me retorna o nome físico da tabela no banco de dados, que no meu caso foi ##TMPSC00_209.

Esse nome sempre vai variar de acordo com as tabelas temporárias já existentes no banco.

Uma observação bacana é que você pode executar um comando select nessa tabela via sua IDE SQL. Segue exemplo:

Agora que a tabela já está preenchida, é só utilizar em suas customizações!

Você ainda pode utilizá-la dentro do Protheus para demais consultas SQL, incluindo JOINS, como no exemplo a seguir:

_cAlias     := GetNextAlias()
cTblName    := '%' + oTable:GetRealName() + '%'

// Executa a consulta SQL
BeginSql Alias _cAlias

    %NoParser%
    SELECT * FROM %Exp:cTblName% TRB
    INNER JOIN %TABLE:SA1% SA1 ON
        SA1.A1_FILIAL = %xFilial:SA1% AND
        SA1.A1_COD = TRB.TMP_CLIENT AND
        SA1.A1_LOJA = TRB.TMP_LOJA AND
        SA1.%NOTDEL%

EndSql

(_cAlias)->(DbGoTop())

// Percorro a consulta SQL
While(!(_cAlias)->(EoF()))

    ConOut('Título: ' + (_cAlias)->TMP_NUM + ' - Razão Social: ' + (_cAlias)->A1_NOME)

    (_cAlias)->(DbSkip())

EndDo

(_cAlias)->(DbCloseArea())

Nesse exemplo, estou fazendo um join da minha tabela temporária de títulos com a minha tabela de clientes. Dessa forma posso buscar outros dados que não estejam em minha tabela temporária. Aqui as possibilidades são várias!

Delete

Por fim, após realizar todos os processamentos desejados, basta chamar o método Delete para efetuar a deleção da tabela temporária do banco de dados. Esse método chama de forma automática a função DbCloseArea para fechar o seu alias.

If(Type('oTable') <> 'U')

    oTable:Delete()
    
    FreeObj(oTable)

EndIf

Espero que a dica tenha sido útil!

Referências

Facebook Comments Box