SQLMIX e a sintaxe SQL do PostgreSQL

Forum sobre SQL.

Moderador: Moderadores

Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 8163
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 6 vezes
Curtiram: 1 vez

SQLMIX e a sintaxe SQL do PostgreSQL

Mensagem por Itamar M. Lins Jr. »

Olá!
Comecei a fazer por aqui:
Ja fiz upload do estoque, com mudanças dos nomes dos campos, não todos.
fiz alguns ajustes para UTF8, pq dá erro se não olharmos isto. Preferi não mudar o padrão que é UTF8 do DATABASE.
Alguma telas e MSG em Hwgui, eu removi.
O importante é que podemos escrever o SQL, vejam o TEXTO, com espaços e observações, sem nenhum problema para o Harbour e o SQLMIX.
TEXT TO VAR cSQL
...
ENDTEXT


A carga é beeem rápida, pega de 100 em 100 os itens.
Detalhe que essa parte chatinha de ficar colocando as estruturas foi a IA que fez, a IA fez quase 100% dessa função.
Só fiz monitorar, pq sempre tem erros. Coaching de IA :lol:

Código: Selecionar todos

#include "dbinfo.ch"
#xcommand TEXT TO VAR <var> => #pragma __stream|<var>:=%s
#xcommand ENDTEXT => #pragma __endtext

Function Main
CriaDbPGSQL()
Return .t.
...

lRet := rddInfo( RDDI_EXECUTE, "DROP TABLE IF EXISTS estoques" )
IF lRet
   alert('Apagou a tabela estoques.')
ELSE
   alert('Falha apagando a tabela estoques')
   RDDINFO(RDDI_DISCONNECT, nCon)
   RDDSETDEFAULT( cRdd )
   RETURN .T.
ENDIF
...
Function CriaDbPGSQL()
...
Text TO VAR cSQL
CREATE TABLE IF NOT EXISTS estoques (
    -- Chave Primária Auto-Incremento (Equivalente ao I:+)
    idproduto INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
   etc ...
    -- Campos Lógicos (Equivalente ao L)
    marcado BOOLEAN DEFAULT FALSE,
    m2 BOOLEAN DEFAULT FALSE,
    m3 BOOLEAN DEFAULT FALSE,
    etc ...
    -- Campos de Texto de Tamanho Fixo (Códigos e Chaves)
    cProd CHAR(20), -- Antigo cod_mercad 13 caractere
    gtin CHAR(14), -- Antigo cod_fiscal
    codcompart CHAR(20),
    idgrupo CHAR(06), -- Antigo cod_grupo
    NCM CHAR(08), -- Antigo cod_ncm
    etc ...
    -- Campos de Texto Variável (Descrições - Otimiza espaço em disco)
    xProd VARCHAR(60), -- Antigo mercadoria
    grupo VARCHAR(25),
    etc ...
    -- Campos Numéricos Decimais (Precisão Comercial Exata)
    quantidade NUMERIC(14,3) DEFAULT 0.000,
    qtdaentreg NUMERIC(14,3) DEFAULT 0.000,
    qtd_baixar NUMERIC(14,3) DEFAULT 0.000,
   etc ...
    -- Campos de Valores Monetários, Custos e Índices
    aliqu_icms NUMERIC(06,2) DEFAULT 0.00,
    desconto NUMERIC(06,2) DEFAULT 0.00,
   etc ...
   -- Campos de Data (Equivalente ao D)
    validade DATE,
    dtapromini DATE,
   etc ...
)
ENDTEXT

lRet := rddInfo( RDDI_EXECUTE, cSQL)
IF lRet
   alert('Criou a tabela estoques !')
ELSE
   alert('Falha na criação da tabela estoques !')
   RDDINFO(RDDI_DISCONNECT, nCon)
   RDDSETDEFAULT( cRdd )
   RETURN .T.
ENDIF

ImportaDbfParaPostgres()
dbUseArea( .T., "SQLMIX" , "select * from estoques",'xx'  )
Browse()
ShowEstruturaTabela( 'estoques' )

Return .t.

// --------------------------------------------------------------------------
// Função interna que processa o loop em lotes e atualiza a tela
// --------------------------------------------------------------------------
STATIC PROCEDURE ProcessaMigracao(oDlg, oGauge, oLabel, nTotalReg, nLote)
    LOCAL cSqlInsert := ""
    LOCAL cValues    := ""
    LOCAL nContador  := 0
    LOCAL nProcessados := 0
    LOCAL cErroPostgres := ""
    dbSelectArea("DBF_EST")
    dbGoTop()

    DO WHILE !EOF()
        nContador++
        nProcessados++

        // Se for o início de um novo lote, monta o cabeçalho do INSERT
        IF nContador == 1
            cSqlInsert := "INSERT INTO estoques ( "
            cSqlInsert += "marcado, cProd, gtin, codcompart, xProd, quantidade, qtdaentreg, "
              etc ...
            cSqlInsert += "aluga, status, diamod, digital, enviado ) VALUES "
        ENDIF

        // Monta a dupla de valores deste registro específico do DBF
        cValues := "( "
        cValues += SqlVal(DBF_EST->marcado)    + ", "
        cValues += SqlVal(DBF_EST->cod_mercad) + ", "
        cValues += SqlVal(DBF_EST->cod_fiscal) + ", "
        cValues += SqlVal(DBF_EST->codcompart) + ", "
        cValues += SqlVal(DBF_EST->mercadoria) + ", "
        cValues += SqlVal(DBF_EST->quantidade) + ", "
        cValues += SqlVal(DBF_EST->qtdaentreg) + ", "
        etc ...
        cValues += SqlVal(DBF_EST->aluga)      + ", "
        cValues += SqlVal(DBF_EST->status)     + ", "
        cValues += SqlVal(DBF_EST->diamod)     + ", "
        cValues += SqlVal(DBF_EST->digital)    + ", "
        cValues += SqlVal(DBF_EST->enviado)    + " )"

        cSqlInsert += cValues

        // Se atingiu o tamanho do lote ou chegou ao fim do arquivo
        IF nContador == nLote .OR. nProcessados == nTotalReg
            // Descarrega o lote inteiro de uma vez no banco
            lRet := rddInfo(RDDI_EXECUTE, cSqlInsert)

            IF lRet
            ELSE
               cErroPostgres := rddInfo(RDDI_ERROR)
               IF Empty(cErroPostgres)
                  // Se ainda sumir, tente esta outra constante comum no SQLMix:
                   // #define RDDI_ERRORMSG 28
                  cErroPostgres := rddInfo( 28 )
               ENDIF
               Alert( "Erro no banco de dados: " + cErroPostgres )
               alert('Falha na inclusão: ' + str(nProcessados))
               hb_memowrit('sql.txt',cSqlInsert)
               cancel
               exit
            ENDIF

            nContador := 0 // Reseta o acumulador do lote
        ELSE
            cSqlInsert += ", " // Adiciona a vírgula para separar a próxima linha/registro
        ENDIF
        dbSelectArea("DBF_EST")
        dbSkip()
    ENDDO

    dbCloseArea()

    alert("Sucesso! Processamento de " + AllTrim(Str(nProcessados)) + " registros concluido.")
RETURN
Captura_de_tela_20260722_134408.png
Captura_de_tela_20260722_134408.png (5.06 KiB) Exibido 59 vezes
Captura_de_tela_20260722_134459.png
Captura_de_tela_20260722_134459.png (97.78 KiB) Exibido 59 vezes
Captura_de_tela_20260722_134540.png
Captura_de_tela_20260722_134540.png (26.7 KiB) Exibido 59 vezes
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 8163
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 6 vezes
Curtiram: 1 vez

Re: SQLMIX e a sintaxe SQL do PostgreSQL

Mensagem por Itamar M. Lins Jr. »

Olá!
A função que converte do DBF para o padrão do PostgreSQL. hb_StrToUTF8 Sem essa mudança ele não migra.
Podemos mudar a criação da tabela para ser igual ao padrão do DBF. PTISO -> (ISO-8859-1 ou Latin1) se não quer usar UTF8.

Código: Selecionar todos


// --------------------------------------------------------------------------
// Função Auxiliar que formata os dados do Harbour para a sintaxe aceita pelo SQL
// --------------------------------------------------------------------------
STATIC FUNCTION SqlVal( xValor )
    LOCAL cRet := ""
    LOCAL cTipo := ValType(xValor)

    IF cTipo == "C"
        // Escapa aspas simples duplicando-as para não quebrar a string SQL
        cRet := "'" + StrTran(AllTrim(hb_StrToUTF8(xValor)), "'", "''") + "'"
    ELSEIF cTipo == "N"
        cRet := AllTrim(Str(xValor, 16, 5))
    ELSEIF cTipo == "D"
        IF Empty(xValor)
            cRet := "NULL"
        ELSE
            // Formata no padrão ANSI do PostgreSQL (AAAA-MM-DD)
            cRet := "'" + DToS(xValor) + "'"
            cRet := "'" + Left(DToS(xValor),4) + "-" + SubStr(DToS(xValor),5,2) + "-" + Right(DToS(xValor),2) + "'"
        ENDIF
    ELSEIF cTipo == "L"
        cRet := If(xValor, "TRUE", "FALSE")
    ELSE
        cRet := "NULL"
    ENDIF
RETURN cRet
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20517
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP
Curtiram: 2 vezes

Re: SQLMIX e a sintaxe SQL do PostgreSQL

Mensagem por JoséQuintas »

Tem certeza que essa conversão funciona pra data vazia ?

Em todo caso, pode economizar fonte usando hb_Dtoc( dData, "YYYY-MM-DD")
E isso funciona pra qualquer formato de data.
José M. C. Quintas
Harbour 3.2, mingw, multithread, gtwvg, fivewin 25.12, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui), (hmg3), (hmg extended), (oohg), PNotepad, ASP, (Linux/Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 8163
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 6 vezes
Curtiram: 1 vez

Re: SQLMIX e a sintaxe SQL do PostgreSQL

Mensagem por Itamar M. Lins Jr. »

Olá!
JoséQuintas escreveu: 22 Jul 2026 15:27 Tem certeza que essa conversão funciona pra data vazia ?
Eu não tenho certeza. Eu sei que a função empty() pega NULL/NIL.
Em todo caso, pode economizar fonte usando hb_Dtoc( dData, "YYYY-MM-DD")
Eu uso assim, mas a IA, ainda não sabe disso :lol:
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 8163
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 6 vezes
Curtiram: 1 vez

Re: SQLMIX e a sintaxe SQL do PostgreSQL

Mensagem por Itamar M. Lins Jr. »

Olá!
Na verdade tô aqui só enrolando. :mrgreen:
Eu quero é fazer a parte LEFT JOIN da NFe ou do Cliente com as DIVERSAS tabelas... E ver como eu vou paginar isso, pra ficar igual ou melhor em velocidade que o LetoDBf. Pq no LetoDBf não existe esperar, nem erro com DBF. Mas deve ficar melhor ainda, acredito.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 8163
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 6 vezes
Curtiram: 1 vez

Re: SQLMIX e a sintaxe SQL do PostgreSQL

Mensagem por Itamar M. Lins Jr. »

Olá!
Videozinho com Hwgui no Linux. O mesmo código para Windows.

Vejam a velocidade de upload da base de dados para um servidor remoto.
Observem o sinal WIRELESS só uma linha.
Saudações,
Itamar M. Lins Jr.
alxsts
Colaborador
Colaborador
Mensagens: 3167
Registrado em: 12 Ago 2008 15:50
Localização: São Paulo-SP-Brasil
Curtiram: 5 vezes

Re: SQLMIX e a sintaxe SQL do PostgreSQL

Mensagem por alxsts »

Olá!

Uma ideia de como montar paginação fornecida pela IA do Google:
Aqui está como implementar o Keyset Pagination no Harbour utilizando a RDD SQLMIX com PostgreSQL.Como o Harbour trabalha de forma procedural e sequencial, nós guardamos o ID do último registro processado em uma variável para utilizá-lo no filtro (WHERE) da próxima consulta.Exemplo Prático em Harbourharbour

Código: Selecionar todos

#include "sqlmix.ch"

PROCEDURE Main()
    LOCAL cConnString, hConn
    LOCAL nUltimoId := 0
    LOCAL nItensPagina := 100

    // 1. Conexão com o PostgreSQL via SQLMIX
    cConnString := "PG:host=localhost;db=meubanco;uid=postgres;pwd=minhasenha"
    hConn := RddInfo( RDDI_CONNECT, { "SQLMIX", cConnString } )

    IF Empty( hConn )
        ? "Erro ao conectar ao banco de dados: " + RddInfo( RDDI_ERROR )
        RETURN
    ENDIF

    ? "--- CARREGANDO PÁGINA 1 ---"
    // Na primeira página, nUltimoId inicia em 0
    nUltimoId := BuscarPagina( nUltimoId, nItensPagina )

    ? ""
    ? "Pressione qualquer tecla para ir para a PÁGINA 2..."
    Inkey(0)
    Cls

    ? "--- CARREGANDO PÁGINA 2 ---"
    // Passamos o ID da última linha da Página 1 para buscar as próximas 100
    IF nUltimoId > 0
        nUltimoId := BuscarPagina( nUltimoId, nItensPagina )
    ELSE
        ? "Não há mais registros."
    ENDIF

    // Fecha a conexão
    RddInfo( RDDI_DISCONNECT, hConn )
RETURN

// Função que executa o SELECT paginado
FUNCTION BuscarPagina( nIdAnterior, nLimite )
    LOCAL cQuery, nUltimoIdEncontrado := 0
    LOCAL nContador := 0

    // Monta a query utilizando o Keyset (WHERE id > nIdAnterior)
    cQuery := "SELECT id, nome FROM clientes " + ;
              "WHERE id > " + AllTrim(Str(nIdAnterior)) + " " + ;
              "ORDER BY id ASC " + ;
              "LIMIT " + AllTrim(Str(nLimite))

    // Executa a Query abrindo uma área de trabalho temporária
    DBUseArea( .T., "SQLMIX", cQuery, "TEMP_PAG" )

    IF NetErr()
        ? "Erro ao executar a consulta."
        RETURN 0
    ENDIF

    // Percorre os 100 registros retornados
    DO WHILE ! TEMP_PAG->( Eof() )
        nContador++
        
        // Exibe os dados na tela
        ? "Reg " + AllTrim(Str(nContador)) + " - ID: " + AllTrim(Str(TEMP_PAG->id)) + " | Nome: " + TEMP_PAG->nome
        
        // Guarda o ID do registro atual (o último loop terá o maior ID)
        nUltimoIdEncontrado := TEMP_PAG->id
        
        TEMP_PAG->( DbSkip() )
    ENDDO

    TEMP_PAG->( DbCloseArea() )

    // Retorna o ID da centésima linha para ser usado na próxima chamada
    RETURN nUltimoIdEncontrado
Use o código com cuidado.

Explicação dos Pontos Chave

- Query Dinâmica: O Harbour remonta a string SQL a cada página inserindo o valor numérico de nIdAnterior diretamente no WHERE id > ....

- Desempenho no PostgreSQL: O comando DBUseArea traz da rede apenas as 100 linhas exatas que você pediu, economizando memória no servidor e no terminal Clipper/Harbour.

- Controle do Cursor: A variável nUltimoIdEncontrado captura o valor do campo id logo antes de fechar a tabela temporária (DbCloseArea), garantindo o ponteiro para a próxima página.
[]´s
Alexandre Santos (AlxSts)
Responder