Página 5 de 7

Tutorial de ADO

Enviado: 23 Ago 2017 06:02
por JoséQuintas
Só pro texto ficar num lugar mais adequado:

ADO é como se fosse uma RDD, pra ler qualquer base de dados.
Neste caso o "driver" de Excel permite trabalhar com a planilha como se fosse banco de dados.
No ADO tem também conector pra TXT, XML, etc.
Trabalha-se como qualquer banco de dados, igual pra todos.

No caso do Harbour, as RDDs permitem trabalhar "estilo DBF".
No caso do ADO, não sei dizer se é "estilo Access" ou estilo ADO mesmo.
Ele tem suas propriedades e métodos, do mesmo jeito que o Harbour tem pra DBF.

Recordset é a tabela, como se fosse um arquivo DBF.
Tem-se as funções :Eof(), :MoveNext(), :MovePrevicous(), :RecordCount(), etc.

Cada registro, igual o DBF tem seus campos, acessados por Fields()
:Name, :Value, etc.

Gerar o recordset (temporário) com o resultado de uma consulta

Código: Selecionar todos

Rs := Conexao:Execute( "SELECT * FROM tabela" )
Trabalhar com recordset

Código: Selecionar todos

? "total de registros:" + Str( Rs:RecourdCount() )
? "Total de campos:" + Str( Rs:Fields:Count() )
DO WHILE ! Rs:Eof()
   ? Rs:Fields(0):Value // o primeiro item é 0 e não 1
   Rs:MoveNext()
ENDDO
Acessar os campos

Código: Selecionar todos

? Rs:Fields(1):Value
? rs:Fields( "CODIGO" ):Value
rs:Fields( "CODIGO" ) = valor
rs:Edit()
rs:Update()
rs:Append()
É um acesso universal a qualquer base de dados, praticamente um RDD universal.
Não é muito divulgado no Harbour porque é formato proprietário da Microsoft, e mais específico pra Windows.

Simplificando: temos acesso a qualquer base de dados no Windows... mas é Windows. Até Excel está incluso nisso.
O uso é livre, mas não é Open Source, e talvez não tenha igual em Linux.

Talvez até isso tenha sido considerado como "A Microsoft querendo dominar o mercado"....rs
Enquanto isso permite usar qualquer base de dados GRÁTIS, no Harbour indicam produtos comerciais PAGOS pra ficar igual DBF.

ADO é só a parte intermediária, o ODBC é que vai dar o tratamento final, uma espécie de driver. Sempre vai depender de existir um "driver" para o banco de dados, quanto mais compatível com ADO melhor. Por isso nem toda base de dados funciona a plena carga no ADO, porque não fizeram "tão compatível". Alguns, como os primeiros ODBC pra dBASE, eram somente pra leitura.
Daria até pra criar um aplicativo somente com ADO e nada mais, usando o formato do ADO pras bases de dados.
Não foi feito pra isso, mas é possível usar, substituindo DBFs por ADO.

Esta parte já seria pra fazer atualizações no recordset, ou até na base de dados, no estilo do ADO.
Como eu disse antes, tudo depende do ODBC ("driver") ter sido criado totalmente compatível com ADO, o que é difícil.
Mas comando SQL sempre funciona.

Código: Selecionar todos

rs:Fields( "CODIGO" ) = valor
rs:Edit()
rs:Update()
rs:Append()
rs:Find()
Esta última parte acabei NUNCA usando.
Mais informações, no site Microsoft, pra lista completa de métodos e propriedades, com muita coisa que NUNCA iremos usar.

Faltou aqui da conexão, ou do arquivo físico de ADO:

Código: Selecionar todos

Cn := win_OleCreateObject( "ADODB.Connection" )
Rs := win_OleCreateObject( "ADODB.Recordset" )
:Open()
:Close()
:ConnectionString

Tutorial de ADO

Enviado: 08 Mar 2018 12:44
por asimoes
Voltado ao assunto:

Tenho uma consulta que retorna 8000 linhas, acontece o seguinte toda vez que o método execute é feito vem a ampulheta do windows de demora e a famosa mensagem do windows "Não está respondendo" Isso é muito chato ao meu ver, porque da impressão para o usuário que o sistema está falhando, no final o browser é carregado, existe alguma forma de resolver isso ? Notem que até os componentes da tela somem!
2018-03-08 12_40_41-.png

Tutorial de ADO

Enviado: 08 Mar 2018 14:06
por JoséQuintas
Talvez fazer o processo numa janela invisível.
Sem janela não vai ter esse efeito.

Tutorial de ADO

Enviado: 08 Mar 2018 15:00
por sygecom
asimoes,
Não seria o caso de colocar uma paginação, pra ir trazendo de pouco em pouco os registros ? Tipo 500 por pagina...

Tutorial de ADO

Enviado: 08 Mar 2018 21:10
por asimoes
Tentei até por thread mais não funcionou,

O incrível é 8000 registros coisa boba para dbf rs

Tutorial de ADO

Enviado: 08 Mar 2018 21:41
por asimoes
Pensando uma conta aqui

Para saber o número de linhas da tabela faria antes select count(*) from tabela
8000 / 500 = 16


Teria que montar um vetor com isso
E limitando por rownum >= e <=

SELECT * FROM TABELA WHERE ... FILTROS AND ROWNUM >= Vetor[1] and rownnum <= Vetor[2]

Vai fazer 16 selects



Essa tabela eu alimento um dbf temporário

Código: Selecionar todos

Vetor
01	0001	0501
02	0502	1002
03	1003	1503
04	1504	2004
05	2005	2505
06	2506	3006
07	3007	3507
08	3508	4008
09	4009	4509
10	4510	5010
11	5011	5511
12	5512	6012
13	6013	6513
14	6514	7014
15	7015	7515
16	7516	8016 esse aqui seria o caso de fixar a conta para 8000

To viajando aqui, não sei se vai ficar legal, rs

Tutorial de ADO

Enviado: 08 Mar 2018 22:54
por alxsts
Olá

Não seria o caso de melhorar a performance desta consulta? Verifique índices, chaves...

Para paginação, tem o exemplo DICA: PAGINAÇÃO EM CONSULTAS SQL

Seria o caso de colocar em uma procedure e passar os limites por parametro.

Tutorial de ADO

Enviado: 09 Mar 2018 09:09
por JoséQuintas
É mesmo, faltou a pergunta principal: de quanta demora estamos falando? quantos segundos?
Precisa mesmo todos os campos? SELECT * FROM...

Tutorial de ADO

Enviado: 09 Mar 2018 15:50
por asimoes
Pessoal,

Consegui montar a query fazendo paginação, logo mais eu posto aqui, respondendo ao Quintas, +- 40 segundos, depende também da utilização do banco.

Consegui trazer todos os registros sem o incomodo "não está respondendo"

Tutorial de ADO

Enviado: 09 Mar 2018 19:58
por asimoes
AdoSel1 - função que traz uma linha
Exportando para tabela temporária em memória

Com isso resolvi o problema "não está respondendo" e até ficou mais rápido, antes +- 30 seg agora 6 seg.

Código: Selecionar todos

   cQuery := "SELECT Count(*) TOTAL FROM EXPEDIENTE.EXPEDIENTE"  
   
   IF AdoSel1( @cRecordSet, @aDados, cQuery, , @cErro ) = 0
      nTotal := aDados[1] // vetor contendo o total de linhas da tabela
      IF nTotal > 500
         aRowNum := {}
         nTotalSelect := nTotal / 1000 // traz 1000 linhas por vez até acabar
         nLinSel := 1          
         FOR I:=1 TO nTotalSelect
            aAdd( aRowNum, {nLinSel, nLinSel += 1000} )
            nLinSel ++
         NEXT
         aRowNum[nTotalSelect, 2] := nTotal
      ENDIF
   ENDIF
   
   
   cQuery := "SELECT *                          "
   cQuery += "FROM ( SELECT topn.*, ROWNUM rnum "
   cQuery += "FROM ( SELECT  E.*                "
   cQuery += "FROM EXPEDIENTE.EXPEDIENTE E      "
   cQuery += "ORDER BY ROWID ) topn             "
   cQuery += "WHERE ROWNUM <= ? )               " // LINHA FINAL
   cQuery += "WHERE rnum  >= ?                  " // LINHA INICIAL   
   
   FOR EACH oElemento IN aRowNum
      aResult := {}
      aChave  := { oElemento[2], oElemento[1] }
      IF AdoSelect( @cRecordSet, @aResult, cQuery, aChave, , @cErro ) = 0
         FOR EACH oElementoQuery IN aResult
            hwg_WriteStatus( ThisForm, 1, 'Aguarde... ' + Hb_NtoS( oElemento:__EnumIndex ) + "/" + Hb_NtoS( Len( aRowNum ) ) )
            ORGAO->( DBSeek( oElementoQuery[04] ) )
            aAdd( aExpediente, { oElementoQuery[05], ;
                                 SubStr( oElementoQuery[03], 5, 3 ) + SubStr( oElementoQuery[03], 1, 4 ), ;  
                                 Win_OemToAnsi( ORGAO->Descricao ), ; // Win_OemToAnsi para exibir caracteres acentuados no browse HwGui
                                 Win_OemToAnsi( oElementoQuery[08] ), ;
                                 Win_OemToAnsi( oElementoQuery[09] ), ;
                                 Win_OemToAnsi( oElementoQuery[10] ), ;
                                 IF(oElementoQuery[12] = '1', 'Sim','Não') } )
            hwg_DoEvents() 
         NEXT
      ENDIF    
   NEXT 

Tutorial de ADO

Enviado: 31 Mai 2018 18:34
por asimoes
Quintas,

Como é que eu faço para criar uma query que faça leitura de registros de 1000 em 1000 até o fim da tabela ?

Tutorial de ADO

Enviado: 02 Jun 2018 00:56
por JoséQuintas
asimoes escreveu:Como é que eu faço para criar uma query que faça leitura de registros de 1000 em 1000 até o fim da tabela ?
Com MySQL é: limit x,y

por exemlo:

limit 1, 1000
limit 1001, 2000
etc

Tutorial de ADO

Enviado: 02 Jun 2018 10:45
por asimoes
O exemplo tem 25404 registros, modifiquei o conteúdo de alguns campos por se tratar de dados de clientes, vejam o tempo que leva para ler a tabela.


Tutorial de ADO

Enviado: 02 Jun 2018 11:10
por asimoes
Se não quiser usar a variável nRecno pode usar ResultSet:AbsolutePosition

Código: Selecionar todos

        IF Mod( ResultSet:AbsolutePosition, 1000 ) = 0 .OR. ResultSet:AbsolutePosition = 1
            @ 11,00 SAY "Tempo Lendo     : " + SecToTime( Seconds() - nSeconds )
            @ 12,00 SAY "Total Registros : " + Transform( nTot, "99999999")
            @ 13,00 SAY "Evento          : " + Transform( ResultSet:AbsolutePosition, "99999999")
            @ 13,Col() + 2 SAY Transform( ( ResultSet:AbsolutePosition / nTot ) * 100, "999.99%" )
         ENDIF

Tutorial de ADO

Enviado: 02 Jun 2018 13:30
por asimoes
O que eu notei é que nos primeiros 1000 registros é rápido +- 10 segundos depois esse tempo vai aumentando até chegar aproximadamente 4 minutos ou mais a cada ciclo de 1000 registros.