Cuidado com o SQLMIX

Projeto [x]Harbour - Compilador de código aberto compatível com o Clipper.

Moderador: Moderadores

Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Cuidado com o SQLMIX

Mensagem por Vlademiro »

Tenham cuidado quando forem usar o SQLMIX com campos que podem variar de tamanho, ou mesmo terem o valor NIL.

O exemplo abaixo funciona perfeitamente... Isso porque todos os campos estão preenchidos.

Código: Selecionar todos

#require "rddsql"

REQUEST SQLMIX

PROCEDURE Main()

   CLS

   rddSetDefault( "SQLMIX" )
   dbCreate( "persons", { { "NAME", "C", 20, 0 }, { "FAMILYNAME", "C", 20, 0 }, { "BIRTH", "D", 8, 0 }, { "AMOUNT", "N", 9, 2 } }, , .T., "persons" )

   APPEND BLANK 
   REPLACE NAME WITH "USER 01"
   REPLACE FAMILYNAME WITH "FAMILLY 01"
   REPLACE BIRTH WITH DATE()
   REPLACE AMOUNT WITH 1000
   APPEND BLANK 
   REPLACE NAME WITH "USER 02"
   REPLACE FAMILYNAME WITH "FAMILLY 02"
   REPLACE BIRTH WITH DATE()
   REPLACE AMOUNT WITH 1200
   APPEND BLANK 
   REPLACE NAME WITH "" 
   REPLACE FAMILYNAME WITH "FAMILLY 03"
   REPLACE BIRTH WITH DATE()
   REPLACE AMOUNT WITH 1200
  
   dbGoTop()	
   Browse()

   RETURN

Se eu substituir o
REPLACE NAME WITH "USER 01"
Por
REPLACE NAME WITH ""

A primeira coluna vai sumir

Isso acontece porque nos DBFs tradicionais os campos sempre tem o mesmo tamanho.
Um campo CHARACTER(30) se estiver vazio vai ter tamanho 30.

Mas o SQLMIX não trata o tamanho dos campos conforme definido em dbCreate().
Se você não gravar os espaços o tamanho do campo vai variar e o TBrowser tradicional (criado
para manipular DBFs) vai se atrapalhar.

Eu tive a oportunidade de usar o SQLMIX com RDD para postgres (sddpgsql) e tinha sempre que gravar
espaços porque dava erro ao recuperar campos NULL. Eu não tinha atentado para a causa desse problema.
Não sei se esse problema persiste quando ele acessa banco de dados porque só uso SQLMIX para trabalhar
com arrays. Minhas funções de banco de dados eu criei a parte e utilizo o SQLMIX para receber os valores.

Ainda acho que vale a pena usar o RDD por causa da facilidade com que as consultas são feitas, podemos
usar o que aprendemos com os DBFs e realmente fazer a diferença.

Mas só temos que ter cuidado com essas surpresas.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Cuidado com o SQLMIX

Mensagem por JoséQuintas »

Interessante....

Ponto negativo para SQLMIX.
É limitar a usar somente o que foi criado pelo próprio SQLMIX.
É ficar eternamente preso a DBF e com mentalidade de DBF.... deixando tudo mais lento do que deveria ser...

Pra quem não entendeu:

Um campo pode ser VARCHAR(500), isso significa que ele pode ter NO MÁXIMO 500 caracteres, mas pode não ter NADA.
No estilo DBF, ele SEMPRE vai ocupar 500 bytes, mas no estilo VARCHAR ele pode até ocupar ZERO espaço.
Espaço em disco não é problema? de certa forma não, mas isso representa espaço em disco, tempo de rede, tempo de processamento, espaço pra backup, etc.
Quanto mais registros e mais espaços vazios, maior vai ser o desperdício ao usar estilo DBF.

Importante dizer:
SQL é mais rápido que DBF, mesmo desperdiçando espaço.
Ao trocar de DBF pra SQL, tudo vai ser mais rápido.
Mas se economizar esse espaço, vai ficar mais rápido ainda.


A explicação disso:

A consulta é igual um arquivo temporário.
É criada uma estrutura pra esse arquivo temporário, pra armazenar o resultado das consultas.
Se até cálculos a gente pode fazer nas consultas, com certeza a estrutura não é a mesma da tabela.
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
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Cuidado com o SQLMIX

Mensagem por Itamar M. Lins Jr. »

Ola!

Código: Selecionar todos

2009-01-26 22:45 UTC+0100 Francesco Saverio Giudice (info/at/fsgiudice.com)
  * harbour/source/rdd/usrrdd/rdds/arrayrdd.prg
    * fixed AR_CREATE() to be compatible with dbCreate() extension
      parameters
  * harbour/source/rdd/usrrdd/example/exarr.prg
    * updated
porque só uso SQLMIX para trabalhar
com arrays.
Se for apenas para isso, não precisa, a SQLMIX usa a classe "ArrayRdd.prg", use o RDD que o próprio SQLMIX usa. Ou use na memória, "mem:"

Código: Selecionar todos

REQUEST HB_MEMIO

PROCEDURE Main()

   LOCAL tmp

   dbCreate( "mem:test", { { "F1", "N", 9, 0 } }, , .T., "memarea" )
   FOR tmp := 1 TO 1000
      dbAppend()
      FIELD->F1 := hb_Random() * 1000000
   NEXT
   INDEX ON FIELD->F1 TAG f1
   dbEval( {|| QOut( FIELD->F1 ) } )
   dbCloseArea()
   dbDrop( "mem:test" )  /* Free memory resource */

   RETURN
Uma vez que "acredito" o SQLMIX não foi criado para ser utilizado dessa forma.
Não encontrei diferença do SQLMIX com qualquer outro... No mais é o gosto de cada um.
Poupa até DO WHILE... Para jogar em ARRAY. Para quem usa TBROWSE é mais simples.
Eu gostei do ADO quando ativado o recurso de gravar direto no BD, evitando o comando "UPDATE, INSERT..."
Mas é questão de gosto. Já li do José Quintas, que esse recurso falha.
No grupo OOHG, outros reclamam do SQLMIX exatamente pq, temos que digitar os comandos tudo em SQL. (vai entender)
Repare, que no exemplo do SQLMIX, ele usa PADR já sabendo dessa situação.

Código: Selecionar todos

#require "rddsql"

REQUEST SQLMIX

PROCEDURE Main()

   CLS

#if defined( __HBSCRIPT__HBSHELL )
   rddRegister( "SQLBASE" )
   rddRegister( "SQLMIX" )
#endif

   rddSetDefault( "SQLMIX" )
   dbCreate( "persons", { { "NAME", "C", 20, 0 }, { "FAMILYNAME", "C", 20, 0 }, { "BIRTH", "D", 8, 0 }, { "AMOUNT", "N", 9, 2 } }, , .T., "persons" )

   dbAppend(); AEval( { PadR( "Bil", 20 ),  PadR( "Gatwick", 20 ),  hb_SToD( "19650124" ), 123456.78 }, {| X, Y | FieldPut( Y, X ) } )
   dbAppend(); AEval( { PadR( "Tom", 20 ),  PadR( "Heathrow", 20 ), hb_SToD( "19870512" ),   9086.54 }, {| X, Y | FieldPut( Y, X ) } )
   dbAppend(); AEval( { PadR( "John", 20 ), PadR( "Weber", 20 ),    hb_SToD( "19750306" ),   2975.45 }, {| X, Y | FieldPut( Y, X ) } )
   dbAppend(); AEval( { PadR( "Sim", 20 ),  PadR( "Simsom", 20 ),   hb_SToD( "19930705" ),  32975.37 }, {| X, Y | FieldPut( Y, X ) } )

   dbGoTop()
   Browse()

   INDEX ON FIELD->AMOUNT TO amount
   dbGoTop()
   Browse()
   dbCloseAll()

   RETURN
O arquivo ArrayRdd.prg fica na pasta contribs\rddmisc

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Cuidado com o SQLMIX

Mensagem por Vlademiro »

Não vou deixar de usar o SQLMIX para simular um DBF através de arrays, já tenho a noção do que pode acontecer e já faço os tratamentos disso no sistema. Estava usando para preencher os controles na HMG e nunca deu problema porque faço o tratamento quando a consulta me retorna um campo nulo.

Essa semana fui usar em um aplicativo modo texto e deu esse problema. Por isso resolvi postar para alertar o pessoal. Vai que alguém desavisado resolve usar para preencher um TBrowseDB ou até mesmo um simples Browse() ...

O interessante é que o problema se apresenta apenas quando o campo é uma string de tamanho zero "" ou NIL, quando é data nula ou numérico zero isso não acontece. Até sei o porquê. Porque um campo com uma string vazia não era uma situação normal há 40 anos, por isso o TBrowse ou DBEdit já pressupõe que o campo caractere está preenchido. Um campo poderia ser uma data nula ou um numérico zero. Mas não existia campo com string com tamanho zero ou campos NIL.

Isso pode gerar problemas chatos em situações mais complexas, tipo quando essa rotina está dentro de uma função escondida em uma lib não sei aonde. Aí fica intermitente. Se o programador testar com a primeira ocorrência preenchida não dá problema, mas se o primeiro campo por acaso vier vazio ("") a coluna toda vai "sumir" no TBrowseDB().

No mais foi só para avisar para quem acompanha o fórum e está pensando em usar esse método para simular DBFs.
Na minha classe que usa o TBrowse eu faço assim :

Código: Selecionar todos

    FOR n := 1 TO LEN( ::aField )
//        oCol := TBColumnNew( ::aCaption[n] , FIELDWBLOCK( ::aField[n] , SELECT() ) )
        cBlock := 'PADR( TRANSFORM( ' + ::aField[n] + ', "' + ::aPicture[n] + '" ) , ' + hb_ntos(::aWidth[ n ]) + ' )'
        cBlock := "{||" + cBlock + "}"
        oCol := TBColumnNew( ::aCaption[n] , &cBlock )

        IF ::aWidth[ n ] <> -1 // Usuario não definiu
            oCol:width := ::aWidth[ n ]
        ENDIF    
        ::oBrw:addColumn( oCol )
    NEXT
::aField é um array com o nome dos campos no DBF Virtual
::aWidth é o tamanho
::aPicture é a máscara

O trecho comentado no código é como estava antes do erro.

Já na HMG ou outros ambientes que não exigem string de tamanho maior que zero eu não precisei mecher, apenas evito atribuir um NIL. Para evitar o NIL eu olho o tipo de dado do controle e faço a conversão necessária com uma função que criei.

Valeu pela dica do DBF na memória, eu já tinha testado com eles mas na época o SQLMIX me pareceu melhor porque não precisa ficar limpando a memória com dbDROP(). No caso do SQLMIx o DBF virtual é limpo pelo coletor de lixo automático. No meu caso, que uso como complemento ao SQL é até bom que a SQLMIX se comporte assim, pois evita espaços em branco extras quando o campo é retornado da Query.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Cuidado com o SQLMIX

Mensagem por JoséQuintas »

Itamar M. Lins Jr. escreveu:Repare, que no exemplo do SQLMIX, ele usa PADR já sabendo dessa situação.
Então, isso é desperdiçar recurso, porque está ocupando espaço no banco de dados, e também na rede/internet pra transferência.

A vantagem do SQLMIX é usar igual DBF, mas.... nesse caso é desvantagem.

No final, todos tem sua vantagem e desvantagem, e vai justamente de criar alguma coisa intermediária pra contornar isso.

QUALQUER que seja a lib intermediária, são tipos de informação que não existem em dbf.
NULL e string de tamanho variável não existe em DBF.
Por isso gostei da classe que criei, porque o resultado acaba sendo SEMPRE o esperado.

Código: Selecionar todos

WITH OBJECT cnSQL
   :cSQL := "SELECT ... "
   :Execute()
   DO WHILE ! :Eof()
      ? :String( "NOME", 50 )
     ? :Number( "CODIGO" )
      ? :Date( "DATA" )
      :MoveNext()
   ENDDO
   :Close()
ENDWITH
Se é pra usar Pad().... não tem nada de anormal usar :String()
Mas uma coisa é ajustar na leitura, e outra coisa é ajustar na gravação.

Ajustando na leitura, funciona pra qualquer base existente, não precisa ser uma base criada no Harbour.

Se não me engano, existe a opção no ODBC de fazer esse ajuste, se elimina ou não os espaços em branco, acho que o próprio ODBC faz o ajuste e não o servidor.
Mesmo assim, ainda resta o NULL.

Ainda tem outro detalhe, que resolvi com minha classe:
Suponha a seguinte situação: "SELECT * FROM CLIENTES WHERE CODIGO < 0 .AND. CODIGO = 1"
Isso retorna.... NADA
NADA não tem estrutura.
Isso pode acabar causando ERRO em qualquer conector.
Com minha classe intermediária não !!!

Mas ok, é tudo coisa que a gente vai contornando de acordo com o que a gente usa, pra facilitar as coisas.
Provavelmente hbmysql retorna um array vazio, e SQLMIX já não sei.

Na minha classe, ao pedir :String( "NOME", 30 ), primeiro eu testo se existe retorno, e se não existir já retorno space(30), uma string vazia, já que essa função está pedindo uma string de 30 posições, acabo nem testando o nome do campo, porque não existe retorno.

O conector, seja ele qual for, não faz milagre, temos que contornar do jeito que der.

Usar ADO acaba ajudando a entender até o porquê desse tipo de coisa.

Pra quem não entendeu:
Digamos que é criada uma estrutura para o "arquivo temporário", com os campos do "arquivo temporário".
Mas se o "arquivo temporário" é vazio.... não tem nem estrutura, porque não tem informação nenhuma.
No caso do hbmysql, que retorna array, seria um array vazio.
Mas como representar um array vazio num dbf? Um DBF sem campo nenhum?
Sem registros dá, mas sem campo nenhum impossível, a não ser que seja criado um campo fake, só pro "DBF" existir.

Importante: É só pra exemplo. NÃO existe arquivo ou DBF, é apenas uma "tradução" por parte do conector pra parecer que existe. É que fica mais fácil explicar/entender fazendo uma comparação com algo conhecido.

Conclusão:
Dá praticamente no mesmo, todos vão ter algum comportamento diferente, por causa de informações diferentes, e vai de cada programador preparar sua "biblioteca"/seu modo de trabalho conforme o que escolher.

Antes de terminar este texto, o Vlademiro fez a postagem, e confirmou exatamente isso: criou seu modo de trabalho
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
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Cuidado com o SQLMIX

Mensagem por Itamar M. Lins Jr. »

Ola!
Eu não entendi então. Pensei que era só para criar a ARRAY. No caso pensei que usava ADO, ou outros.
Mas mesmo assim pode comutar entre um RDD e outro, para cria o DBF.
Acredito que é proposital, exatamente por conta do VARCHAR.

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Cuidado com o SQLMIX

Mensagem por Itamar M. Lins Jr. »

Ola!
Então, isso é desperdiçar recurso, porque está ocupando espaço no banco de dados, e também na rede/internet pra transferência.
A vantagem do SQLMIX é usar igual DBF, mas.... nesse caso é desvantagem.
No final, todos tem sua vantagem e desvantagem, e vai justamente de criar alguma coisa intermediária pra contornar isso.
Não é.
Vc não entendeu ainda. Isso é por conta do TBROWSE.
Não tem nada com rede. SQLMIX ou LetoDbf usa o que o MOTOR(SGBD) usa.
Posso falar no caso do LetoDbf, que ZIPA e CRIPTOGRAFA de ponta a ponta a conexão.
A mesma coisa penso eu, que o MySQL faz, MariaDb, etc... para diminuir o tráfego, a parte de descompactar/descriptografar fica na estação "FORA" do HUB/REDE.
Pegue ai o TBROWSE(até na Hwgui), crie um ARRAY com campo de tamanho variado e tente mostrar ele, tanto faz com ADO ou com SQLMIX. Vai cortar pelo menor ou maior vai depender de quem será o primeiro.
E isso é feito no lado do cliente, depois que a query chega.

Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Cuidado com o SQLMIX

Mensagem por JoséQuintas »

Itamar M. Lins Jr. escreveu:Não é.
Vc não entendeu ainda. Isso é por conta do TBROWSE.
Ok, deu a impressão de que seria obrigatório na gravação.

Acho até que o tbrowse tem opção de definir um tamanho pra coluna independente do conteúdo.
É que tradicionalmente acostumamos a deixar no modo automático, conforme conteúdo, e isso só dá mais certo com DBF.
Essa seria a forma de ambiente GUI, definindo tamanho na tela e relatórios, independente do conteúdo gravado.
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
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Cuidado com o SQLMIX

Mensagem por Itamar M. Lins Jr. »

Ola!
Mas como representar um array vazio num dbf? Um DBF sem campo nenhum?
Sem registros dá, mas sem campo nenhum impossível, a não ser que seja criado um campo fake, só pro "DBF" existir.
Já que saiu do DBF já está até esquecendo.
O DBF(array/mem) é criado com o retorno, com o que tiver na sentença da QUERY.
Desde quando DBF quando não tem nada cria campo fake ?
O DBF existe na memória! a estrutura dele, e LastRec()/RecCount() retorna zero(0). Usamos o que sabemos, não estamos inventando moda.


Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Cuidado com o SQLMIX

Mensagem por JoséQuintas »

Itamar M. Lins Jr. escreveu:Já que saiu do DBF já está até esquecendo.
O DBF(array/mem) é criado com o retorno, com o que tiver na sentença da QUERY.
Desde quando DBF quando não tem nada cria campo fake ?
O DBF existe na memória! a estrutura dele, e LastRec()/RecCount() retorna zero(0). Usamos o que sabemos, não estamos inventando moda.
Não lembro se Isto é válido

dbCreate( "vazio.dbf", {} )
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
Vlademiro
Usuário Nível 4
Usuário Nível 4
Mensagens: 752
Registrado em: 11 Jul 2005 02:46

Cuidado com o SQLMIX

Mensagem por Vlademiro »

Nesse caso específico tem que testar com reccount().
Avatar do usuário
Itamar M. Lins Jr.
Administrador
Administrador
Mensagens: 7928
Registrado em: 30 Mai 2007 11:31
Localização: Ilheus Bahia
Curtiu: 1 vez

Cuidado com o SQLMIX

Mensagem por Itamar M. Lins Jr. »

Ola!
dbCreate( "vazio.dbf", {} )
Embora não entenda quase nada de C, acredito que ele primeiro cria a estrutura do DBF(array), baseado, nesta parte.
E não na função dbcreate()

Código: Selecionar todos

   for( uiCount = 0; uiCount < uiFields; uiCount++ )
...
   {
      DBFIELDINFO dbFieldInfo;

      pMyField = mysql_fetch_field_direct( pSDDData->pResult, uiCount );

      memset( &dbFieldInfo, 0, sizeof( dbFieldInfo ) );
      dbFieldInfo.atomName = pMyField->name;
      dbFieldInfo.uiLen    = ( HB_USHORT ) pMyField->length;

      switch( pMyField->type )
      {
         case MYSQL_TYPE_TINY:
         case MYSQL_TYPE_SHORT:
            dbFieldInfo.uiType = HB_FT_INTEGER;
            break;

         case MYSQL_TYPE_LONG:
         case MYSQL_TYPE_LONGLONG:
         case MYSQL_TYPE_INT24:
            dbFieldInfo.uiType = HB_FT_LONG;
            break;

         case MYSQL_TYPE_DECIMAL:
         case MYSQL_TYPE_NEWDECIMAL:
         case MYSQL_TYPE_FLOAT:
         case MYSQL_TYPE_DOUBLE:
            dbFieldInfo.uiType = HB_FT_DOUBLE;
            dbFieldInfo.uiDec  = ( HB_USHORT ) pMyField->decimals;
            break;

         case MYSQL_TYPE_STRING:
         case MYSQL_TYPE_VAR_STRING:
         case MYSQL_TYPE_ENUM:
            dbFieldInfo.uiType = HB_FT_STRING;
            break;
...
Saudações,
Itamar M. Lins Jr.
Saudações,
Itamar M. Lins Jr.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Cuidado com o SQLMIX

Mensagem por JoséQuintas »

Então... no final ADO e SQLMIX dá quase no mesmo.

O SQLMIX usa DBF? Com certeza não, senão seria um lixo.
Usar igual DBF é uma coisa, converter pra DBF é outra. Converter pra DBF é demorado.
SQLMIX apenas usa o resultado igual DBF, ele NÃO converte pra DBF.

Mas o mundo xbase é engraçado....

Vou sair do DBF.... usar SQL igual DBF
Vou sair do Harbour... usar uma outra linguagem xbase
Vou sair do console... usar HWGUI com a opção CLIPPER pra se comportar igual console

Sei lá.... parece coisa de doido kkkk
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

Cuidado com o SQLMIX

Mensagem por JoséQuintas »

Eu também passei essa fase, olha meu SQL de antigamente:

Código: Selecionar todos

      cnSQL:cSQL := "SELECT * FROM INFORME WHERE ANOBASE = " + StrZero( Year( dDataInforme ), 4 ) + ;
         " AND LOCOD = " + NumberSQL( XLOCOD ) + " AND COD <> 0"
      cnSQL:Execute()
      cTempDbf := cnSQL:SQLToDbf()
      SELECT 0
      USE ( cTempDbf ) ALIAS TEMP
      cTempCdx = MyTempFile( "CDX" )
      INDEX ON temp->MES TAG ( "MES" ) TO ( cTempCdx )
      INDEX ON Str( temp->COD, 7 ) + Str( temp->MES, 2 ) TAG ( "LOCAT" ) TO ( cTempCdx )
      OrdSetFocus( "LOCAT" )
      GOTO TOP
eu convertia pra DBF antes de usar.
A rotina:

Código: Selecionar todos

   // Somente string vém tamanho correto em DefinedSize, Int vém como 10, e depende da versão do ODBC

METHOD SQLToDbf( oStructure ) CLASS ADOClass

   LOCAL nSelect, cDbfFile, nCont, oElement

   ::Execute()

   IF ::Rs == NIL
      IF oStructure == NIL
         oStructure := { { "NONE", "C", 5, 0 } }
      ENDIF
   ENDIF
   IF oStructure == NIL
      oStructure := {}
      FOR nCont = 1 TO ::Rs:Fields:Count()
         AAdd( oStructure, ::FStru( nCont - 1 ) )
      NEXT
   ELSE
      FOR EACH oElement IN oStructure
         IF Len( oElement ) < 4
            AAdd( oElement, 0 )
         ENDIF
      NEXT
   ENDIF
   nSelect  := Select()
   cDbfFile := MyTempFile( "DBF" )
   SELECT 0
   dbCreate( cDbfFile, oStructure )
   USE ( cDbfFile ) ALIAS SQLToDbf
   DO WHILE ! ::Rs:Eof()
      RecAppend()
      FOR nCont = 1 TO Len( oStructure )
         DO CASE
         CASE oStructure[ nCont, 2 ] == "N" ; FieldPut( nCont, ::Number( oStructure[ nCont, 1 ] ) )
         CASE oStructure[ nCont, 2 ] == "D" ; FieldPut( nCont, ::Date( oStructure[ nCont, 1 ] ) )
         OTHERWISE                          ; FieldPut( nCont, ::String( oStructure[ nCont, 1 ] ) )
         ENDCASE
      NEXT
      ::Rs:MoveNext()
   ENDDO
   USE
   SELECT ( nSelect )
   ::CloseRecordset()

   RETURN cDbfFile
A lista de tipos de campo é grande

Código: Selecionar todos

#define AD_EMPTY                        0
#define AD_TINYINT                      16
#define AD_SMALLINT                     2
#define AD_INTEGER                      3
#define AD_BIGINT                       20
#define AD_UNSIGNEDTINYINT              17
#define AD_UNSIGNEDSMALLINT             18
#define AD_UNSIGNEDINT                  19
#define AD_UNSIGNEDBIGINT               21
#define AD_SINGLE                       4
#define AD_DOUBLE                       5
#define AD_CURRENCY                     6
#define AD_DECIMAL                      14
#define AD_NUMERIC                      131
#define AD_BOOLEAN                      11
#define AD_ERROR                        10
#define AD_USERDEFINED                  132
#define AD_VARIANT                      12
#define AD_IDISPATCH                    9
#define AD_IUNKNOWN                     13
#define AD_GUID                         72
#define AD_DATE                         7
#define AD_DBDATE                       133
#define AD_DBTIME                       134
#define AD_DBTIMESTAMP                  135
#define AD_BSTR                         8
#define AD_CHAR                         129
#define AD_VARCHAR                      200
#define AD_LONGVARCHAR                  201
#define AD_WCHAR                        130
#define AD_VARWCHAR                     202
#define AD_LONGVARWCHAR                 203
#define AD_BINARY                       128
#define AD_VARBINARY                    204
#define AD_LONGVARBINARY                205
#define AD_CHAPTER                      136
#define AD_FILETIME                     64
#define AD_PROPVARIANT                  138
#define AD_VARNUMERIC                   139
#define AD_ARRAY                        /* &H2000 */
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

Cuidado com o SQLMIX

Mensagem por JoséQuintas »

Podemos converter assim:

Código: Selecionar todos

METHOD FStru( xField ) CLASS ADOClass

   LOCAL cName, cType, nLen, nDec, nType

   IF ::Rs == NIL
      RETURN { "NIL", "C", 0, 0 }
   ENDIF
   cName := Upper( Trim( ::Rs:Fields( xField ):Name ) )
   nType := ::Rs:Fields( xField ):Type
   DO CASE
   CASE nType == AD_BOOLEAN
      cType := "L"
      nLen  := 1
      nDec  := 0
   CASE hb_AScan( { AD_DATE, AD_DBDATE, AD_DBTIME, AD_DBTIMESTAMP }, nType ) != 0
      cType := "D"
      nLen  := 8
      nDec  := 0
   CASE hb_AScan( { AD_BIGINT, AD_SMALLINT, AD_TINYINT, AD_INTEGER, AD_UNSIGNEDTINYINT, AD_UNSIGNEDSMALLINT, AD_UNSIGNEDINT, AD_UNSIGNEDBIGINT }, nType ) != 0
      cType := "N"
      nLen  := ::rs:Fields( xField ):Precision
      nDec  := 0
   CASE hb_AScan( { AD_DOUBLE, AD_SINGLE }, nType ) != 0
      cType := "N"
      nLen  := ::Rs:Fields( xField ):Precision
      nDec  := ::Rs:Fields( xField ):NumericScale
   CASE nType == AD_CURRENCY
      cType := "N"
      nLen  := ::Rs:Fields( xField ):Precision
      nDec  := ::Rs:Fields( xField ):NumericScale
   CASE hb_AScan( { AD_DECIMAL, AD_NUMERIC, AD_VARNUMERIC }, nType ) != 0
      cType := "N"
      nLen  := ::Rs:Fields( xField ):Precision
      nDec  := ::Rs:Fields( xField ):NumericScale
   CASE hb_AScan( { AD_BSTR, AD_CHAR, AD_VARCHAR, AD_LONGVARCHAR, AD_WCHAR, AD_VARWCHAR, AD_LONGVARWCHAR }, nType ) > 0
      cType := "C"
      nLen := ::Rs:Fields( xField ):DefinedSize
      IF nLen > 255
         cType := "M"
         nLen := 10
      ENDIF
      nDec := 0
   CASE hb_AScan( { AD_BINARY, AD_VARBINARY, AD_LONGVARBINARY }, xField ) != 0
      cType := "M"
      nLen  := 10
      nDec  := 0
   OTHERWISE
      MsgExclamation( "Undefined ADO Type " + Ltrim( Str( ::Rs:Fields( xField ):Type ) ) )
      cType := "M"
      nLen  := 10
      nDec  := 0
   ENDCASE
   IF cType == "N"
      nLen := iif( nLen < 0, 13, nLen )
      nLen := iif( nLen > 15, 15, nLen )
      nDec := iif( nDec < 0, 6, nDec )
      nDec := iif( nDec > 6, 6, nDec )
      IF nDec != 0
         nLen := nLen + nDec + 1
      ENDIF
   ENDIF

   RETURN { cName, cType, nLen, nDec }
ou assim:

Código: Selecionar todos

METHOD Value( xField ) CLASS ADOClass

   LOCAL xValue, cType

   cType := ::FType( xField )
   DO CASE
   CASE cType == "N"
      xValue := ::Number( xField )
   CASE cType == "D"
      xValue := ::Date( xField )
   CASE cType == "C"
      xValue := ::String( xField )
   OTHERWISE
      xValue := ::String( xField )
   ENDCASE

   RETURN xValue
Sei lá... antes de converter exatamente para o campo, é bom acostumar a usar, por exemplo variável DateTime e outros.
Senão... ao invés de solução, cria mais problemas...
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/
Responder