Tutorial de ADO

Aqui você poderá oferecer suas Contribuições, Dicas e Tutoriais (Texto ou Vídeo) que sejam de interesse de todos.

Moderador: Moderadores

alxsts
Colaborador
Colaborador
Mensagens: 3092
Registrado em: 12 Ago 2008 15:50
Localização: São Paulo-SP-Brasil

Tutorial de ADO

Mensagem por alxsts »

Olá!

Primeiro, troque esta macro por um code block:

Código: Selecionar todos

LOCAL bWhile := { || ! EOF() }

While Eval( bWhile )
   ...
End
Isto aqui consome bastante tempo e recursos:

Código: Selecionar todos

03	      @ 06,16 say str(WQTD)
04	      @ 07,19 say CARRO
SQL foi desenhado para funcionar com grupos de registros. Na base do "um por um" fica lento.

Monte blocos, por exemplo de 200 registros e insira de uma vez...

Código: Selecionar todos

#include "ado.ch"

PROCEDURE DbfImport()

   LOCAL bWhile, nCount, cString, cSQL, nQtd, nTotal
   
   bWhile := { || <condição while> }
   
   cString := "INSERT INTO QTRACS (CARRO, MCT, LATITUDE, LONGITUDE, DATA, HORA, VELOC, LOCAL) VALUES "
   
   nCount := 0
   
   nQtd := 200
   
   While Eval( bWhile )
   
      If nCount == 0
         cSql := cString
      Endif
   
      cSql += "( '" + alltrim(CARRO) + "', "
      cSql += "'" + alltrim(MCT) + "', "
      cSql += "'" + alltrim(LATITUDE) + "', "
      cSql += "'" + alltrim(LONGITUDE) + "', "
      cSql += "'" + Transform( ( dtos( DATA ), "@R 9999-99-99" ) + "', "
      cSql += "'" + alltrim(HORA) + "', "
      cSql += "'" + alltrim(alltrim(str(VELOC))) + "', "
      cSql += "'" + alltrim(LOCAL) + "' ) "

      nCount += 1

      If nCount == nQtd
         cSql += ";"
         oConexao:Execute( cSql )

         nTotal += nCount
         nCount := 0
         cSql := "" 

         DispOutAt( 06, 16, Str( nTotal ) )  // Evita carregar o Get System (@... SAY)
         DispOutAt( 07, 19, CARRO )
      Else
         cSql += ", "
      Endif
   
      DbSkip()
   End

   If ! Empty( cSql )
      oConexao:Execute( cSql )
   Endif

   DispOutAt( 06, 16, Str( nTotal ) + " Registros incluidos" )

   DbCloseAll()

   If oConexao:state == adStateOpen
      oConexao:close()
   Endif

   oConexao := NIL

RETURN
Não testei...
[]´s
Alexandre Santos (AlxSts)
sasquast
Usuário Nível 1
Usuário Nível 1
Mensagens: 29
Registrado em: 31 Jul 2019 16:47
Localização: Volta Redonda / RJ

Tutorial de ADO

Mensagem por sasquast »

Muito obrigado pelas dicas.

Irei modificar o fonte e fazer alguns testes.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Tutorial de ADO

Mensagem por JoséQuintas »

Isto pode ser alterado, mas o default do MySQL é comando até 4MB, caso contrário dá erro.

É como já foi descrito, mas, apenas pra ficar mais didático:

Código: Selecionar todos

cn:Execute( "INSERT INTO tabela ( lista de campos ) VALUES ( lista de valores ), ( lista de valores ), ( lista de valores )" )
cn:Execute( "INSERT INTO tabela ( lista de campos ) VALUES ( lista de valores ), ( lista de valores ), ( lista de valores )" )
Também já foi dito, a diferença é brutal, reduz de horas pra minutos.
Quanto mais inserir por vez, mais rápido será.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, 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
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Tutorial de ADO

Mensagem por JoséQuintas »

Sobre as conversões:

Eu preferi criar funções StringSQL(), NumberSQL(), DateSQL(), etc., pra padronizar a formatação, e ValueSQL() quando o tipo é conhecido.
E em casos desse tipo, criar array com a lista de campos.
Apenas pra dar uma idéia

Código: Selecionar todos

aList := { ;
   { "CODIGO", dbf->codigo }, ;
  { "NOME", dbf->nome }, ;
   { "VALOR", dbf->valor } }

cSQL := "INSERT INTO tabela ("
FOR EACH aCampo IN aList
   cSQL += aCampo[ 1 ]
   IF ! aCampo:__EnumIsLast()
      cSQL += ","
   NEXT
NEXT
cSQL += ") VALUES ("
FOR EACH aCampo IN aList
   cSQL += ValueSQL( aCampo[ 2 ] )
   IF aCampo:__EnumIsLast()
      cSQL += ","
   ENDIF
NEXT
cSQL += ")"
cn:Execute( cSQL )
...
FUNCTION ValueSQL( xValue )

LOCAL cType 

cType := ValType( xValue )

DO CASE
CASE cType == "D"
   IF Empty( xValue )
      xValue := "NULL"
   ELSE
      xValue := [']  + Transform( Dtos( xValue ), "@R 9999-99-99" ) + ["]
   ENDIF
CASE cType == "N"
   xValue := Ltrim( Str( xValue ) )
CASE cType == "C"
   xValue := ['] + AllTrim( xValue ) + [']
ENDCASE

RETURN xValue
Com o tempo, pode ir acrescentando outras opções, como por exemplo na string trocar ' por \' , Chr(13) por "\" + Chr(13), e outras mais.
Melhor alterar uma função do que fonte por fonte.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, 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/
sasquast
Usuário Nível 1
Usuário Nível 1
Mensagens: 29
Registrado em: 31 Jul 2019 16:47
Localização: Volta Redonda / RJ

Tutorial de ADO

Mensagem por sasquast »

Os 4 minutos se tornaram 40 segundos.

Muito obrigado. Ainda há melhoria a ser feita mas vocês me deram o caminho.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Tutorial de ADO

Mensagem por JoséQuintas »

sasquast escreveu:Os 4 minutos se tornaram 40 segundos.
Muito obrigado. Ainda há melhoria a ser feita mas vocês me deram o caminho.
Aproveitando...

1) É por isso que aproveitar fonte nem sempre é a melhor opção, porque pode estar perdendo velocidade

2) Fazer uma comparação entre DBF e SQL usando a mesma programação não dá. Dependendo como comparar o resultado é diferente

3) Só pra chamar a atenção: o ADO está sendo apenas um intermediário pra enviar o comando para o servidor, o servidor recebe a string e faz o serviço.
O que sempre chamei a atenção para o ADO é sobre isso, porque é usar sem depender de outras LIBs.
E se quiser trocar o ADO depois, é só usar qualquer coisa que repasse o comando para o servidor, não vai ter perdido o que aprendeu com ADO.
Na verdade ele tem muito mais recursos que esse, mas acho que nem vale a pena se aprofundar, pra não ficar dependente dele.

Comentário adicional:
Sempre usei assim sem problemas.
Mas.... dá pra indicar um parâmetro no Execute() pra indicar que nesse caso não vai ter retorno.
Segundo a documentação do ADO, isso economiza recursos, porque o ADO não precisa criar nada extra pra receber o retorno.

https://docs.microsoft.com/pt-br/sql/ad ... rver-ver15
ado.png
Editado pela última vez por JoséQuintas em 03 Mai 2022 16:32, em um total de 1 vez.
Razão: corrigida referência pra ado.connection e não ado.command
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, 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
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Tutorial de ADO

Mensagem por JoséQuintas »

Faltou:

Código: Selecionar todos

#define AD_EXECUTE_NORECORDS           128
exemplo:

Código: Selecionar todos

         ::Cn:Execute( cSQL, , AD_EXECUTE_NORECORDS )
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, 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/
alxsts
Colaborador
Colaborador
Mensagens: 3092
Registrado em: 12 Ago 2008 15:50
Localização: São Paulo-SP-Brasil

Tutorial de ADO

Mensagem por alxsts »

Olá!

Postando o código novamente pois havia alguns erros:

Código: Selecionar todos

#include "ado.ch"

PROCEDURE DbfImport()

   LOCAL bWhile, nCount, cString, cSQL, nQtd, nTotal
   
   bWhile := { || <condição while> }
   
   cString := "INSERT INTO QTRACS (CARRO, MCT, LATITUDE, LONGITUDE, DATA, HORA, VELOC, LOCAL) VALUES "
   nCount  := 0
   nQtd    := 200
   nTotal  := 0

   While Eval( bWhile )
   
      If nCount == 0
         cSQL := cString
      Endif
   
      cSQL += "( " + cs(CARRO) + ", "
      cSQL += cs( MCT) + ", "
      cSQL += cs( LATITUDE) + ", "
      cSQL += cs( LONGITUDE) + ", "
      cSQL += cs( DATA ) + ", "
      cSQL += cs( HORA) + ", "
      cSQL += cs( VELOC) + ", "
      cSQL += cs( LOCAL) + " ) "

      nCount += 1

      If nCount == nQtd
         oConexao:Execute( cSQL + ";",  , AD_EXECUTE_NORECORDS )

         nTotal += nCount
         nCount := 0
         cSQL := "" 

         DispOutAt( 06, 16, Str( nTotal ) )
         DispOutAt( 07, 19, CARRO )
      Else
         cSQL += ", "
      Endif
   
      DbSkip()
   End

   If ! Empty( cSQL )
      // remover a ultima virgula
      cSQL := Alltrim( cSQL )
      cSQL := Substr( cSQL, 1, ( Len( cSQL ) - 1 ) )
      
      oConexao:Execute( cSQL + ";", ,  AD_EXECUTE_NORECORDS )
   Endif

   DispOutAt( 06, 16, Str( nTotal ) + " Registros incluidos" )

   DbCloseAll()

   If oConexao:state == adStateOpen
      oConexao:close()
   Endif

   oConexao := NIL

RETURN
//------------------------------------------------------------------------------

FUNCTION CS( xArg )
RETURN ConvertToSQL( xArg )
//------------------------------------------------------------------------------
FUNCTION ConvertToSQL( xArg )

   LOCAL cRet := "NULL"

   If xArg != NIL
      Switch Valtype( xArg )
         Case "N"  // Numeric
         Case "+"  // Auto increment
         Case "^"  // RowVers 8 Row version number; modification count of this record
         Case "Y"  // Currency 8 64 bit integer with implied 4 decimal
         Case "I"  // Integer 1, 2, 3, 4 or 8 Signed Integer ( Width : )" }
            cRet := LTrim( Str( xArg ) )
            Exit
         Case "C"  // Character
         Case "M"  // Memo
            cRet := "'" + StrTran( AllTrim( xArg ), "'", "''" ) + "'"
            Exit
         Case "D"  // Date
            cRet := "'" + hb_DtoC( xArg, "yyyy-mm-dd" ) + "'"
            Exit
         Case "L"  // Logical  -  MySQL boolean is an alias for tinyint(1)
            cRet := If( xArg, "1", "0" ) 
            Exit
         Case "B"  // B Double 8 Floating point / 64 bit binary
         Case "T"  // Time 4 or 8 Only time (if width is 4 ) or Date & Time (if width is 8 ) (?)
         Case "="  // ModTime 8 Last modified date & time of this record
         Case "@"  // DayTime 8 Date & Time
            Exit
         Otherwise
            cRet := "NULL"
      End Switch
   Endif

RETURN cRet
//------------------------------------------------------------------------------
- Inicialização da variável nTotal
- Eliminar última vírgula da string
- Incorporado o AD_EXECUTE_NORECORDS
- incorporada função CS()
sasquast escreveu:Os 4 minutos se tornaram 40 segundos.
Que bom! Quantos registros? Se aumentar o tamanho do bloco, pode diminuir este tempo, observando o tamanho máximo da string (4000 baytes, segundo postado acima).
JoséQuintas escreveu:Eu preferi criar funções StringSQL(), NumberSQL(), DateSQL(), etc., pra padronizar a formatação, e ValueSQL() quando o tipo é conhecido.
É o ideal. Eu criei uma função única, postada acima. Falta completar todos os tipos de dados.
[]´s
Alexandre Santos (AlxSts)
Responder