Clipper On Line • Ver Tópico - DBF pra MySQL
Página 1 de 2

DBF pra MySQL

MensagemEnviado: 07 Set 2019 12:08
por JoséQuintas
Eu ainda não sei como vou fazer o acesso simultâneo pesado, mas enquanto isso... fazer mais algumas alterações.
Até agora, passei pra MYSQL e eliminei de DBF, mas.... chegando nos que isso complica e/ou deixaria lento.
Ainda não sei se vou duplicar TUDO, ou se continuar movendo...

Fazer agora o de notas fiscais, porque é o que menos é acessado pra gravação/atualização por usuários.
Mas pode ser muito usado em relatórios, e outras rotinas....
Vou começar e ver o que dá, dependendo do que acontecer, decidir o que fazer.

Vou começar agora.
Mostrar o passo a passo, conforme for fazendo.
A intenção é..... SEMPRE poder atualizar no cliente, não importa se terminei ou não.

Primeira alteração:

Confirmar se a base está criada no MySQL, e se tem a estrutura adequada.
Faz tanto tempo, que não faço idéia de como isso está no MySQL.
Para isso, usar os backups de cliente que tenho aqui, pra comparar.

No cliente... tanto faz se existir ou não a tabela no MySQL, então, antes ou depois da alteração, continuará tudo funcionando normalmente.
Com certeza no MySQL vai ter o campo incremental como chave principal, campo que não existe no DBF.

DBF pra MySQL

MensagemEnviado: 07 Set 2019 12:11
por JoséQuintas
Recebo backup por ftp de alguns, vão ser os cobaias para o teste.
O Backup inclui DBF e MySQL.

backupfpt.png

DBF pra MySQL

MensagemEnviado: 07 Set 2019 12:25
por JoséQuintas
Do FTP para a pasta de backups, da pasta de backups instalando na pasta de trabalho de cada empresa.

backupdbf.png


Antes de restaurar no MySQL.... pausa pro almoço.... rs

DBF pra MySQL

MensagemEnviado: 07 Set 2019 18:54
por JoséQuintas
Backup restaurado também no MySQL.
Tudo pronto.
São backups de cliente, neles está assim: parte DBF e parte MySQL.
Retomando a migração.
Talvez melhor fazer logo tudo que falta de uma vez.... vamos ver...

mysql.png

DBF pra MySQL

MensagemEnviado: 07 Set 2019 19:01
por JoséQuintas
Fugindo um pouco do assunto inicial...
Registrando o imposto dos XMLs que passam pelos aplicativos.

cbenef.png


Alguns fornecedores já vém usando o tal código de beneficiamento.
Apenas curiosidade, talvez agora comece a olhar isso com outra finalidade.

DBF pra MySQL

MensagemEnviado: 07 Set 2019 19:59
por JoséQuintas
Pois é... como pode CST 20, com redução, não ter redução? sefazclass não estava pegando....

cbenef.png


Interessante.
Parece que podemos padronizar TODOS os aplicativos e não vimos antes.
E mostra que o CEST foi uma invenção furada, que agora poderia ser jogado no lixo.
O CBENEF substitui muito bem o CEST, apesar que o governo pode manter pra validação, ou pra não admitir que cagou.

DBF pra MySQL

MensagemEnviado: 07 Set 2019 20:13
por JoséQuintas
Mas ainda tem alguma coisa errada...
CEST não é pra produto com substituição tributária? cadê ela?

DBF pra MySQL

MensagemEnviado: 08 Set 2019 02:00
por JoséQuintas
"Atualizando versão" na minha máquina.
Faltam essas empresas.

Carregou o aplicativo, já simultâneo com notas em DBF e MySQL.
Tanto faz consultar notas em DBF ou MySQL.

conversao.png


Agora dá pra dar uma olhada em cada módulo que usa, já alterar pra MySQL.
Se não der pra usar sem relacionamento com outras tabelas, a saída vai ser fazer o mesmo com outras tabelas.
Se instalar nos clientes agora.... não vão usar as notas do MySQL, mas vai estar tudo salvo em DBF + MySQL.

Tem alguma vantagem duplicar tudo?
Sim.
Vou ter tempo de sobra pra atualizar o aplicativo.
E em caso de problemas, vai ser apenas salvar novamente no MySQL (depois de corrigir o possível problema, lógico).

Pensando bem.... avaliar em fazer isso logo com tudo que é tabela, assim acelera o processo, e não precisa fonte intermediário.

DBF pra MySQL

MensagemEnviado: 08 Set 2019 02:16
por JoséQuintas
mysql.png


Não tinha reparado nisso antes.
240MB de DBF ocupando 61MB de MySQL.
É lógico... no MySQL podemos usar o campo VARCHAR, e com isso não grava espaço em branco.
Pois é... mais outra vantagem... economiza disco, rede, internet, etc.

Fico imaginando se a RDDSQL ou similares tiram proveito disso ao trabalhar igual DBF....

DBF pra MySQL

MensagemEnviado: 09 Set 2019 18:32
por JoséQuintas
Como não estava usando essas tabelas, faltava ajustar estrutura...
Ao contrário de DBF.... só acrescentar campos...

      IF ! :FieldExists( "NFFCPVAL", "JPNOTA" )
         :AddField( "NFFCPVAL", "JPNOTA", "DOUBLE(14,2) NOT NULL DEFAULT '0'" )
      ENDIF
      IF ! :FieldExists( "NFAVENUM", "JPNOTA" )
         :AddField( "NFAVENUM", "JPNOTA", "VARCHAR(40) NOT NULL DEFAULT ''" )
      ENDIF
      IF ! :FieldExists( "NFAVEPRO", "JPNOTA" )
         :AddField( "NFAVEPRO", "JPNOTA", "VARCHAR(40) NOT NULL DEFAULT ''" )
      ENDIF
      IF ! :FieldExists( "NFMODFIS", "JPNOTA" )
         :AddField( "NFMODFIS", "JPNOTA", "VARCHAR(2) NOT NULL DEFAULT ''" )
      ENDIF
      IF ! :fieldExists( "NFCADEMI", "JPNOTA" )
         :AddField( "NFCADEMI", "JPNOTA", "VARCHAR(6) NOT NULL DEFAULT ''" )
      ENDIF
      IF ! :FieldExists( "IPPEDCOM", "JPITPED" )
         :AddField( "IPPEDCOM", "JPITPED", "VARCHAR(6) NOT NULL DEFAULT ''" )
      ENDIF
      IF ! :FieldExists( "IPFCPALI", "JPITPED" )
         :AddField( "IPFCPALI", "JPITPED", "DOUBLE(6,2) NOT NULL DEFAULT '0'" )
      ENDIF
      IF ! :FieldExists( "IPFCPVAL", "JPITPED" )
         :AddField( "IPFCPVAL", "JPITPED", "DOUBLE(14,2) NOT NULL DEFAULT '0'" )
      ENDIF
      IF ! :FieldExists( "IPNUMLAN", "JPITPED" )
         :AddField( "IPNUMLAN", "JPITPED", "VARCHAR(6) NOT NULL DEFAULT ''" )
      ENDIF

DBF pra MySQL

MensagemEnviado: 09 Set 2019 18:42
por JoséQuintas
E transferir tabela para o MySQL.
Como precaução, caso misturem versões, ou carreguem o aplicativo trocando versão, testo se a base está vazia antes de começar a transferir.

STATIC FUNCTION jpnotaToMySql()

   LOCAL cnMySql := ADOClass():New( AppcnMySqlLocal() )

   IF AppcnMySqlLocal() == NIL
      RETURN NIL
   ENDIF
   WITH OBJECT cnMySql
      :cSql := "SELECT COUNT(*) AS QTD FROM JPNOTA"
      IF :ReturnValueAndClose( "QTD" ) == 0
         CopyDbfToMySql( "JPNOTA", .T. )
      ENDIF
   ENDWITH
   CLOSE DATABASES

   RETURN NIL


Pronto, notas fiscais em MySQL, sendo atualizadas em simultâneo.

Agora decidir se começo a remover esse DBF do aplicativo, ou se transfiro outros também, assim já removo vários de uma vez.

Porque o arquivo de notas?
Pouca atualização simultânea, ou quase nenhuma, e é um dos maiores DBFs.

Como eu já disse antes, ainda não sei como vou fazer o simultâneo no MySQL, então deixando pro final o que vai precisar de um "algo mais".

Também não decidi sobre relacionamento entre tabelas, e nem pesquisei como fazer.

DBF pra MySQL

MensagemEnviado: 16 Set 2019 09:39
por JoséQuintas
Tanta discussão sobre como acessar o MySQL e esqueci um detalhe importantíssimo:

Se o backup do MySQL chegar a 4GB, não tem como fazer pelo Harbour 32 bits.
Compilar com 64 bits não é problema, o problema é se todas as máquinas vão aceitar isso.

DBF pra MySQL

MensagemEnviado: 17 Set 2019 14:53
por Itamar M. Lins Jr.
Ola!
Pq vc não replica os dados ?
Espelhamento ?
https://imasters.com.br/data/replicacao-mysql-em-5-minutos

Saudações,
Itamar M. Lins Jr.

DBF pra MySQL

MensagemEnviado: 17 Set 2019 15:05
por JoséQuintas
Itamar M. Lins Jr. escreveu:Pq vc não replica os dados ?


A idéia é boa, mas só vale pra quando temos acesso total ao servidor por internet, e ao servidor MySQL.

DBF pra MySQL

MensagemEnviado: 20 Set 2019 20:18
por JoséQuintas
Até agora tudo tranquilo.
Tive problemas, mas por outras alterações que fiz junto.

É que antes guardava cópia de XML na pasta do aplicativo: aplicativo\xml\anomes
São inúteis agora, porque já estão salvos em outro lugar.

Como sou meio paranóico com isso....
Se estão lá até há mais de um ano, porque eu preciso ter pressa em apagar?
Acrescentei pro próprio aplicativo ir pegando esses XMLs, conferindo se realmente estão salvos, e ir apagando do disco.
Um pouco por vez.
Se vai demorar um mês ou mais... tanto faz... vai ser automático, vai ser seguro e não vai atrapalhar o usuário.

DBF pra MySQL

MensagemEnviado: 21 Set 2019 20:51
por JoséQuintas
Aqui fica bem visível o uso da minha classe.
Apesar de ter que alterar o fonte, fica parecido com o fonte anterior.
Gravação do DBF e do MySQL.

STATIC FUNCTION ImportaFinanceiro( oElement, oNFe, mCliCod, nParcela )

   LOCAL IDFinanceiro, cnMySql := ADOClass():New( AppcnMySqlLocal() )

   SELECT jpfinan
   OrdSetFocus( "numlan" )
   IDFinanceiro := NovoCodigo( "jpfinan->fiNumLan" )
   RecAppend()
   REPLACE ;
      jpfinan->fiNumLan  WITH IDFinanceiro, ;
      jpfinan->fiTipLan  WITH iif( oNFE:Emitente:Cnpj == jpempre->emCnpj, "1", "2" ), ;
      jpfinan->fiNumDoc  WITH oNFE:cNumDoc, ; // 9 digitos
      jpfinan->fiDatEmi  WITH oNFE:DataEmissao, ;
      jpfinan->fiParcela WITH StrZero( nParcela, 3 ), ;
      jpfinan->fiCliFor  WITH mCliCod, ;
      jpfinan->fiSacado  WITH mCliCod, ;
      jpfinan->fiValor   WITH oElement:Valor, ;
      jpfinan->fiDatVen  WITH oElement:Vencimento, ;
      jpfinan->fiDatEmi  WITH oNFE:DataEmissao, ;
      jpfinan->fiPedido  WITH jppedi->pdPedido, ;
      jpfinan->fiInfInc  WITH LogInfo()
   RecUnlock()
   WITH OBJECT cnMySql
      :QueryCreate()
      :QueryAdd( "FINUMLAN", IDFinanceiro )
      :QueryAdd( "FITIPLAN", iif( oNFE:Emitente:Cnpj == jpempre->emCnpj, "1", "2" ) )
      :QueryAdd( "FINUMDOC", oNFE:cNumDoc )
      :QueryAdd( "FIDATEMI", oNFE:DataEmissao )
      ;QueryAdd( "FIPARCELA", StrZero( nParcela, 3 ) )
      :QueryAdd( "FICLIFOR", mCliCod )
      :QueryAdd( "FISACADO", mCliCod )
      ;QueryAdd( "FIVALOR"< oElement:Valor )
      :QueryAdd( "FIDATVEN", oELement:Vencimento )
      :QueryAdd( "FIDATEMI", oNFE:DataEmissao )
      :QueryAdd( "FIPEDIDO", jppedi->PdPedido )
      :QueryAdd( "FIINFINC", LogInfo() )
      :QueryExecuteInsert( "JPFINAN" )
   ENDWITH

   RETURN NIL


cnMySql é o canal de comunicação, o "WhatsApp" com o servidor.
:QueryCreate() e :QueryAdd(), apenas criam uma lista
:QueryExecuteInsert() é a que faz todo trabalho de conversar com o servidor.

Tem mais coisas, além disso, nas rotinas, mas comparando com DBF, seria algo como isto:

// QueryCreate()
aList := {}

// QueryAdd()
AAdd( aList, { "CODIGO", 10 } )
AAdd( aList, { "NOME", "JOSE" } )

//QueryExecuteInsert()
RecLock()
FOR EACH oElement IN aList
   REPLACE &( oElement[ 1 ] ) WITH oElement[ 2 ]
   // FieldPut( FIeldNum( oElement[ 1 ] ), oElement[ 2 ] )
NEXT
RecUnLock()


Inclusive.... olhando agora... talvez mais interessante alterar a gravação de DBF pra usar o mesmo array do MySQL.
Desse jeito, o mesmo fonte serviria pra DBF e MySQL, ao invés de duplicar rotina.
A diferença seria só uma linha pra dbf e uma pra MySQL.

:QueryExecuteInsertDBF()
:QueryExecuteInsert()

Interessante.... vou pensar se vale a pena isso.

DBF pra MySQL

MensagemEnviado: 22 Set 2019 02:26
por JoséQuintas
Não sei se vou usar, mas abaixo a aplicação prática do que mencionei

STATIC FUNCTION ImportaFinanceiro( oElement, oNFe, mCliCod, nParcela )

   LOCAL IDFinanceiro, cnMySql := ADOClass():New( AppcnMySqlLocal() )

   SELECT jpfinan
   OrdSetFocus( "numlan" )
   IDFinanceiro := NovoCodigo( "jpfinan->fiNumLan" )
   WITH OBJECT cnMySql
      :QueryCreate()
      :QueryAdd( "FINUMLAN", IDFinanceiro )
      :QueryAdd( "FITIPLAN", iif( oNFE:Emitente:Cnpj == jpempre->emCnpj, "1", "2" ) )
      :QueryAdd( "FINUMDOC", oNFE:cNumDoc )
      :QueryAdd( "FIDATEMI", oNFE:DataEmissao )
      ;QueryAdd( "FIPARCELA", StrZero( nParcela, 3 ) )
      :QueryAdd( "FICLIFOR", mCliCod )
      :QueryAdd( "FISACADO", mCliCod )
      ;QueryAdd( "FIVALOR"< oElement:Valor )
      :QueryAdd( "FIDATVEN", oELement:Vencimento )
      :QueryAdd( "FIDATEMI", oNFE:DataEmissao )
      :QueryAdd( "FIPEDIDO", jppedi->PdPedido )
      :QueryAdd( "FIINFINC", LogInfo() )
      :QueryDBFExecuteInsert()         /// aqui insere no DBF
      :QueryExecuteInsert( "JPFINAN" )   /// aqui insere no MySQL
   ENDWITH

   RETURN NIL

DBF pra MySQL

MensagemEnviado: 22 Set 2019 20:22
por JoséQuintas
Esqueci de dizer uma coisa muitíssimo importante:

Não só por causa da compilação -e3 -es2, mas uso SEMPRE o alias.
jpnota->nfNotFis

Então, pra mudança de jpnota pra MySQL, pesquiso em todos os fontes o uso de jpnota->

E o mesmo para cada um dos arquivos.

Já disse várias vezes aqui:

O que mais fazemos é alterar fonte.
Colocar alias parece perda de tempo, mas ganha-se muito tempo depois em alterações.

Tem gente que pra economizar fonte utiliza for/next:

FOR nCont = 1 TO FCount()
   cNome := FieldName( nCont )
   PUBLIC m&cNome
   m&cNome := &cNome
NEXT
...
FOR nCont = 1 TO FCount()
   cNome := FieldName( nCont )
   REPLACE &cNome WITH m&cNome
NEXT


Pode ganhar tempo criando o fonte, mas depois, pra pesquisar uso de campo ou outra coisa... ferrou.

Infelizmente digo o mesmo sobre array.

Gastem bastante tempo criando novos fontes, e aproveitem alterações pra ajustar um pouco de fontes velhos.
Quanto mais fácil alterar, mais tempo irão economizar.

Passar notas pra MySQL.... só pesquisar aonde usa o arquivo de notas JPNOTA.
Se eu usasse um dos esquemas acima.... já era... muita coisa iria passar desapercebida.

Mais outra:

Durante essa "conversão", muitos fontes não tinham a conexão MySQL.
Nem me preocupei com isso.
Na hora de compilar, usando -w3 -es2, o próprio Harbour me avisou sobre as variáveis de conexão, que não existiam.
Foi só ir acrescentando nos fontes que davam erro de compilação.

Vai que decidem fazer igual eu fiz pra migrar pra MySQL.....
É bom saber que tudo isso ajuda a fazer uma migração mais tranquila.

Como eu sempre digo:
Dar uma geral no fonte antes de ir pra uma coisa nova.
Venho fazendo isso há anos... então tudo fica mais tranquilo.

DBF pra MySQL

MensagemEnviado: 23 Set 2019 04:34
por JoséQuintas
Putz, olhem só a possibilidade que acabei inventando com a rotina anterior:

#define GRAVA_DBF          .T.
#define GRAVA_MYSQL      .T.

WITH OBJECT cnMySql
   :QueryCreate()
   :QueryAdd( "ESNUMLAN", "SALDO" )
   :QueryAdd( "ESNUMDOC", "SALDO" )
   :QueryAdd( "ESITEM",   cItem )
   :QueryAdd( "ESOBS",    "SALDO NESTA DATA" )
   :QueryAdd( "ESTIPLAN", iif( oElement[ 1 ] > 0, "2", "1" ) )
   :QueryAdd( "ESDATLAN", dDataLimite - 1 )
   :QueryAdd( "ESNUMDEP", Str( oElement:__EnumIndex, 1 ) )
   :QueryAdd( "ESQTDE",   Abs( oElement[ 1 ] ) )
   :QueryAdd( "ESVALOR",  Abs( oElement[ 2 ] ) / iif( oElement[ 1 ] == 0, 1, oElement[ 1 ] ) )
   IF GRAVA_DBF
      :DBFQueryExecuteInsert()
   ENDIF
   IF GRAVA_MYSQL
      :QueryExecuteInsert( "JPESTOQ" )
   ENDIF
ENDWITH


Acompanhem comigo:

- Alterar as gravações em DBF para a classe
- Acrescentar a linha que grava simultâneo em MySQL
- Alterar tudo pra usar preferencialmente MySQL
- Retirar toda gravação em DBF
- Apagar DBFs

E o principal: prazo de instalação SEMPRE INSTANTÂNEO A CADA ALTERAÇÃO

Não é fantástico !!!

DBF pra MySQL

MensagemEnviado: 26 Set 2019 09:49
por JoséQuintas
Mais um pra MySQL: PEDIDOS

arquivos.png


Apenas pra curiosidade:

Dos 6 maiores arquivos, 4 passei a salvar no MySQL, isso vai reduzir o tamanho total de DBFs pra menos da metade.

Porque não passei os itens de pedido?
Eles não tem chave única pra referência. Vou criar isso agora, pra no futuro transferir. (talvez semana que vém).

Pois é... tudo indica que de 1GB em DBF vai reduzir pra 50MB...

E se vai sobrar só 50MB... melhor fazer logo tudo de uma vez....
Já pensando nessa idéia de tudo de uma vez.

DBF pra MySQL

MensagemEnviado: 05 Out 2019 21:37
por JoséQuintas
Vixe....
Me empolguei demais, e agora estou mudando de idéia....
Já tem cliente com meio caminho andado, mas talvez refaça....

Por enquanto... como é duplicado em DBF e MySQL, mas com foco no DBF... posso regravar o MySQL à vontade.

Detalhes:

No MySQL só uso campo incremental pra chave, lógico, é o normal e é o melhor uso.
Meu aplicativo é direcionado a chave caractere.
Até agora estava direcionando a conversão pra continuar usando esse campo caractere.... e ia deixar a mudança pra depois no MySQL.
Pois estou pensando em fazer isso ainda no DBF, pra gravar no MYSQL já usando código definitivo, e ao invés de usar minha rotina de numeração, fazer uso do MySQL pra isso, mesmo usando DBF - acho que vai ser mais seguro.
Isso afeta TODO aplicativo, antes mesmo de usar MySQL, e afeta toda conversão, que leva exatamente a estrutura do DBF, o que obriga que o campo chave do MySQL já exista no DBF e que seja numérico.

Pois é....
Agora pensando nas alternativas...
Trata-se inclusive de refazer as conversões que já fiz.
E de deixar tudo pronto pra ser trocado a qualquer hora, esteja como estiver.

DBF pra MySQL

MensagemEnviado: 09 Out 2019 19:37
por JoséQuintas
Desisti de continuar, e voltei atrás.

      :ExecuteCmd( "DROP TABLE IF EXISTS BA_AUTO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS BA_GRUP" )
      :ExecuteCmd( "DROP TABLE IF EXISTS BA_MOVI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTDIARI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTHISTO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTLOTES" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTLANCA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTPLANO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPBAMOVI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPCADAS" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPCLISTA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPCOMISS" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPDOLAR" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPESTOQ" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPFINAN" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPFINANC" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPFORPAG" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPITEM" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPITPED" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPLFISC" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPMDFCAB" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPMDFDET" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPMOTORI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPNOTA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPNUMERO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPPEDI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPPEDIDO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPPRECO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPPRONCM" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPREFCTA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPSENHA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPAGE" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPATI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPINS" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPLOC" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPOPE" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABEL" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTRANSA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTRANSP" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPUF" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPUSRMSG" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPVENDED" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPVEICUL" )
      :ExecuteCmd( "DROP TABLE IF EXISTS RASTREA" )


Não se enganem, NÃO estou zerando tudo.
Isso é só a parte deste mês, e lixos anteriores.
Ainda vão sobrar alguns gigas no MySQL que continuam em uso.

NÃO foi problema com MySQL ou ADO.
É que me empolguei mesmo, agora que estou voltando atrás é que confirmei o quanto exagerei.
Alterar tudo isso no aplicativo AO MESMO TEMPO, foi o que atrapalhou.
Tudo isso, mais estruturas, mais campos chave, e mais outras coisas.

Nenhum problema nos clientes.
TODOS tem coisa que ficou pela metade.
Também não vou ter nenhum problema em voltar atrás.
Lembram? estava duplicando mas funcionando em DBF... se apagar do MySQL... nenhum problema.

Depois da limpeza, fazer UM DE CADA VEZ MESMO.
Me segurar bastante pra me limitar a um DBF por vez.
E também obrigar que clientes atualizem, assim retiro toda parte de apagar tabelas do MySQL.

DBF pra MySQL

MensagemEnviado: 10 Out 2019 00:10
por Fernando queiroz
como terminei a conversão para gráfico, agora chegou a hora da limpeza dos fontes
e partir para -w3 , estou fazendo devagar mas ta ficando 'BUNITO" de ver tudo arrumadinho
depois é partir para um SGBD.
Inclusive estou mudando algumas funções passando parâmetros para facilitar o entendimento
A cabeça já pegou fogo varias vezes :)´ :)´ :)´

DBF pra MySQL

MensagemEnviado: 12 Out 2019 22:57
por JoséQuintas
Pois é...
Eu sempre posto aqui que primeiro precisa organizar tudo antes de partir pra mudanças, e fiz o oposto kkkk

A próxima versão:
Apagando do MySQL
      :ExecuteCmd( "DROP TABLE IF EXISTS BA_AUTO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS BA_GRUP" )
      :ExecuteCmd( "DROP TABLE IF EXISTS BA_MOVI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTDIARI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTHISTO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTLOTES" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTLANCA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS CTPLANO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPANPMOV" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPBAMOVI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPBARRA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPCADAS" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPCLISTA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPCOMISS" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPDOLAR" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPEMPRE" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPESTOQ" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPFINAN" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPFINANC" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPFORPAG" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPIMPOS" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPITEM" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPITPED" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPLFISC" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPLOGNFE" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPMDFCAB" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPMDFDET" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPMOTORI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPNOTA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPNUMERO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPPEDI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPPEDIDO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPPRECO" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPPRONCM" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPREFCTA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPSENHA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPAGE" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPATI" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPINS" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPLOC" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABANPOPE" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTABEL" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTRANSA" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPTRANSP" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPUF" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPUSRMSG" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPVENDED" )
      :ExecuteCmd( "DROP TABLE IF EXISTS JPVEICUL" )
      :ExecuteCmd( "DROP TABLE IF EXISTS RASTREA" )


e apagando de DBF:

   LOCAL cFile, aList := { ;
      "ba_auto.dbf", ;
      "ba_grup.dbf", ;
      "ba_movi.dbf", ;
      "jpanpage.dbf", ;
      "jpanpins.dbf", ;
      "jpanpope.dbf", ;
      "jpbarra.dbf", ;
      "jpcarcor.dbf", ;
      "jpcep.dbf", ;
      "jpcfop.dbf", ;
      "jpcotaca.dbf", ;
      "jpcotcli.dbf", ;
      "jpcotfor.dbf", ;
      "jpcotpro.dbf", ;
      "jpcte.dbf", ;
      "jpdecret.dbf", ;
      "jpdecret.cdx", ;
      "jpdocrel.dbf", ;
      "jpdocrel .dbf", ; // criou errado
      "jpedicfg.dbf", ;
      "jpfisica.dbf", ;
      "jpibpt.dbf", ;
      "jplicmov.dbf", ;
      "jplogsi.dbf", ;
      "jpnfbase.dbf", ;
      "jpnfeger.dbf", ;
      "jpnfexml.dbf", ;
      "jpnotaca.dbf", ;
      "jpordbar.dbf", ;
      "jpordres.dbf", ;
      "jpordser.dbf", ;
      "jpprehis.dbf", ;
      "jppromix.dbf", ;
      "jpromcab.dbf", ;
      "jpromdet.dbf", ;
      "jptexto.dbf", ;
      "jpvvdem.dbf", ;
      "jpvvfin.dbf", ;
      "nfbase.dbf", ;
      "rastrea.dbf" }

   FOR EACH cFile IN aList
      IF File( cFile )
         fErase( cFile )
      ENDIF
   NEXT
   aList := Directory( "tmp*.dbf" )
   FOR EACH cFile IN aList
      fErase( cFile[ F_NAME ] )
   NEXT
   IF ! AppConexao() == NIL
      IF File( "jpreguso.dbf" )
         fErase( "jpreguso.dbf" )
      ENDIF
   ENDIF


Na prática restaram alguns clientes com DBF de aplicativo específico.
Pra esses, que não tem MySQL, o log do aplicativo continua em DBF.

Para os DBFs, comparei as pastas das empresas.

#include "directry.ch"
#include "inkey.ch"

PROCEDURE Main

   LOCAL aList := {}, cPath, aDbfList, oDbf, nPos, oFile

   CLS
   FOR EACH cPath IN ;
      { "assesor", "carbolub", "cicuto", "cordeiro", "crispetrol", ;
        "dricar", "gargantua", "locafacil", "lubrivan", "maringa", ;
        "peaton", "toppetro", "unifila", "waripaer" }
      aDBFList := Directory( "d:\jpa\" + cPath + "\*.dbf" )
      FOR EACH oDBF IN aDBFList
         IF ( nPos := ASCan( aList, { | e | e[ 1 ] == Upper( oDBF[ F_NAME ] ) } ) ) == 0
            AAdd( aList, { Upper( oDBF[ F_NAME ] ), 0, "" } )
            nPos := Len( aList )
         ENDIF
         aList[ nPos, 2 ] += 1
         aList[ nPos, 3 ] += cPath + ","
      NEXT
   NEXT
   FOR EACH oFile IN aList
      IF oFile[ 2 ] != 14
         ? oFile[ 1 ], oFile[ 2 ], oFile[ 3 ]
      ENDIF
   NEXT
   ? "Fim"
   Inkey(0)

   RETURN


Agora fazer algo parecido pra comparar as bases MySQL, pra ver se tem lixo em alguma.

Como é o mesmo aplicativo em todas, mesmo que não usem tudo, pelo menos tem que existir as mesmas bases em DBF e MySQL.
No geral, conversões que eu tinha deixado preparado, ou módulos desativados que não apaguei arquivos antes.

Pois é... agora seguindo meus próprios conselhos de organizar tudo primeiro kkkk

E como a versão apaga até tabelas do MySQL...
Talvez obrigar a troca de versão primeiro. em todos, e depois retirar tudo isso de apagar do MySQL, já que vão entrar tabelas oficiais depois.

DBF pra MySQL

MensagemEnviado: 25 Nov 2019 10:04
por JoséQuintas
Só pro post não ficar sem fim....
Tá em andamento.

Hoje alterando um fluxo de caixa, aqui dá uma boa idéia de DBF e ADO/MySQL

   mTempFile := TempFileArray( 2 )
   IF m_DeAte == 1
      COPY TO ( mTempFile[ 1 ] ) FIELDS idFinan, fiDatVen, fiTipLan, fiNumDoc, fiParcela, ;
         fiDatEmi, fiCliFor, fiValor, fiObs FOR ;
         Empty( jpfinan->fiDatPag ) .AND. Empty( jpfinan->fiDatCan ) ;
         .AND. GrafProc()
   ELSE
      COPY TO ( mTempFile[ 1 ] ) FIELDS idFinan, fiDatVen, fiTipLan, fiNumDoc, fiParcela, ;
         fiDatEmi, fiCliFor, fiValor, fiObs FOR ;
         jpfinan->fiDatVen >= m_Datai .AND. jpfinan->fiDatVen <= m_Dataf ;
         .AND. Empty( jpfinan->fiDatPag ) .AND. Empty( jpfinan->fiDatCan ) ;
         .AND. GrafProc()
   ENDIF
   SELECT 0
   USE ( mTempFile[ 1 ] ) NEW ALIAS temp
   INDEX ON Dtos( temp->fiDatVen ) + temp->fiTipLan + temp->fiNumDoc + temp->fiParcela to ( mTempFile[ 2 ] )
   SELECT temp
   GOTO TOP
   DO WHILE nKey != K_ESC .AND. ! Eof()


Como já comentei, o SQL/ADO é como trabalhar com arquivos temporários.
Neste fonte fica bem nítida a mudança.

      cSql := "SELECT IDFINAN, FIDATVEN, FITIPLAN, FINUMDOC, FIPARCELA, FIDATEMI, FICLIFOR, FIVALOR, FIOBS " + ;
         "FROM JPFINAN " + ;
         "WHERE ISEMPTY( FIDATPAG ) AND ISEMPTY( FIDATCAN )"
      IF m_DeAte == 2
         cSql += " AND FIDATVEN BETWEEN " + DateSql( m_Datai ) + " AND " + DateSql( m_Dataf )
      ENDIF
      cSql += " ORDER BY FIDATVEN, FITIPLAN, FINUMDOC, FIPARCELA"
      Temporario := Conexao:Execute( cSql )
      DO WHILE nKey != K_ESC .AND. ! Temporario:Eof()


Isso acima é sem minha classe, ADO puro.
Além de menos fonte, fica muito mais rápido.

Nota: deixou de ser teste, agora é uso prático mesmo.