Clipper On Line • Ver Tópico - Rotina Harbour pra SQL

Rotina Harbour pra SQL

Discussão sobre SQL

Moderador: Moderadores

 

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 17 Ago 2021 23:06

Um pequeno passo a passo do que estou alterando neste momento.

METHOD SalvaCustoProduto( nIdPedido ) CLASS SubPedidoClass

   LOCAL npdValCus := 0, cnSQL := ADOClass():New( AppConexao() )

   IF nIdPedido == 0
      RETURN NIL
   ENDIF
   WITH OBJECT cnSQL
      :cSQL := "UPDATE JPITPED, JPITEM" + ;
         " SET IPPRECUS = JPITEM.IEULTPRE, IPVALCUS = JPITPED.IPQTDE * JPITEM.IEULTPRE" + ;
         " WHERE IPPEDIDO = " + NumberSQL( nIdPedido ) + " AND JPITPED.IPPRODUTO = JPITEM.IDPRODUTO"
      :ExecuteCmd()
      :cSQL := "SELECT SUM( IPVALCUS ) AS VALCUS" + ;
         " FROM JPITPED" + ;
         " WHERE IPPEDIDO = " + NumberSQL( nIdPedido )
      :Execute()
      nPdValCus += :Number( "VALCUS" )
      :CloseRecordset()

      :QueryCreate()
      :QueryAdd( "PDVALCUS", npdValCus )
      :QueryAdd( "PDINFALT", LogInfo() )
      :QueryExecuteUpdate( "JPPEDIDO", "IDPEDIDO = " + NumberSQL( nIdPedido ) )
   ENDWITH

   RETURN NIL


Isso dá pra simplificar pra dois comandos

METHOD SalvaCustoProduto( nIdPedido ) CLASS SubPedidoClass

   LOCAL cnSQL := ADOClass():New( AppConexao() )

   IF nIdPedido == 0
      RETURN NIL
   ENDIF
   WITH OBJECT cnSQL
      //:ExecuteCmd( "call ze_PedidoSalvaCusto( " + NumberSQL( nIdPedido ) + ", " + StringSQL( LogInfo() ) + " )" )
      :ExecuteCmd( "UPDATE JPITPED, JPITEM" + ;
         " SET IPPRECUS = JPITEM.IEULTPRE, IPVALCUS = JPITPED.IPQTDE * JPITEM.IEULTPRE" + ;
         " WHERE IPPEDIDO = " + NumberSQL( nIdPedido ) + " AND JPITPED.IPPRODUTO = JPITEM.IDPRODUTO" )
      :ExecuteCmd( "UPDATE JPPEDIDO" + ;
            " SET PDVALCUS = ( SELECT SUM( IPVALCUS ) AS VALCUS" + ;
            " FROM JPITPED" + ;
            " WHERE IPPEDIDO = " + NumberSQL( nIdPedido ) + ")," + ;
            " PDINFALT = " + StringSQL( LogInfo() ) + ;
            " WHERE IDPEDIDO = " + NumberSQL( nIdPedido ) )
   ENDWITH

   RETURN NIL


O que está comentado é o que vai ser depois: apenas uma chamada a uma stored procedure

CREATE PROCEDURE ze_PedidoSalvaCusto(
   nIdPedido INT(11),
   cLogInfo VARCHAR(100)
)
BEGIN

IF nIdPedido <> 0 THEN
   UPDATE JPITPED, JPITEM
      SET IPPRECUS = JPITEM.IEULTPRE, IPVALCUS = JPITPED.IPQTDE * JPITEM.IEULTPRE
      WHERE IPPEDIDO = nIdPedido AND JPITPED.IPPRODUTO = JPITEM.IDPRODUTO;
   
   UPDATE JPPEDIDO
      SET PDVALCUS = ( SELECT SUM( IPVALCUS )
         FROM JPITPED
         WHERE IPPEDIDO = nIdPedido ),
      PDINFALT = cLogInfo
      WHERE IDPEDIDO = nIdPedido;
END IF;

END


Esse fonte acima vai ser executado no MySQL, e vai criar a STORED PROCEDURE que vai fazer o cálculo.
A partir daí.... disponível pra qualquer linguagem de programação.

E assim... de pedacinho em pedacinho... apagando fonte Harbour e substituindo por fonte SQL.

A nova rotina em Harbour vai ficar assim:

METHOD SalvaCustoProduto( nIdPedido ) CLASS SubPedidoClass

   LOCAL cnSQL := ADOClass():New( AppConexao() )

   cnSQL:ExecuteCmd( "call ze_PedidoSalvaCusto( " + NumberSQL( nIdPedido ) + ", " + StringSQL( LogInfo() ) + " )" )

   RETURN NIL


Pois é....
trabalhar com Microsoft ADO fica preso a... NADA... só usando recursos do servidor MySQL.

Então....
Foi isso que eu sempre quis dizer sobre o ADO.
Não se trata de ser melhor ou pior.
Apenas não dependemos de ninguém, e vamos dando atenção à parte importante.
É enviar comando e receber resposta (se tiver).
Sem se aprofundar em recursos extras do ADO, assim não fica preso ao ADO.
E todo conhecimento vale pra qualquer coisa depois.
O difícil é depois querer trocar ADO por outra coisa, porque não faz mais diferença kkkk

E nesse caso, tanto faz se o formato fosse DBF, array ou recordset.... não tá usando nenhum deles kkkk
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 18 Ago 2021 08:17

binary.png


Complemento:

Testei aqui na minha máquina, tudo ok.

Cadastrei no jpbinary do meu site.
Significa que ela já vai ser criada no servidor de todos os meus clientes.
Basta que eles cliquem em atualizar, mesmo não existindo novo EXE, a função vai ser criada lá. (ou atualizada depois).

Pois é, ficou simples assim atualizar rotinas no servidor.
Nada mais de ter que gerar e mandar por um EXE novo.

Lógico.... pra fazer uso da rotina, só o EXE novo vai fazer isso.
Mas isso não tem pressa, trata-se de apagar fonte....
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 18 Ago 2021 12:39

Apenas pra efeito de visualização, talvez trocar isto:

   UPDATE JPPEDIDO
      SET PDVALCUS = ( SELECT SUM( IPVALCUS )
         FROM JPITPED
         WHERE IPPEDIDO = nIdPedido )
      WHERE IDPEDIDO = nIdPedido;


por isto:

SELECT SUM( IPVALCUS ) 
   FROM JPITPED
   WHERE IPPEDIDO = nIdPedido
   INTO nTotalCusto;

UPDATE JPPEDIDO SET PDVALCUS = nTotalCusto WHERE IDPEDIDO = nIdPedido;


Neste caso só mesmo em stored procedure.

Ainda estou começando com essa coisa toda, posso estar certo ou errado no meu ponto de vista.
Talvez até precise tratamento de erro nessas coisas...
Apenas mostrando possibilidades
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 19 Ago 2021 20:28

Mais uma

STATIC FUNCTION SituacaoFinanceiro( nIdPedido, mAberto, mAtraso )

   LOCAL cnSQL := ADOClass():new( AppConexao() )

   IF nIdPedido == 0
      RETURN NIL
   ENDIF
   WITH OBJECT cnSQL
      :cSQL := "SELECT SUM( FIVALOR ) AS ABERTO, " + ;
         " SUM( IF( DATE( FIDATVEN ) < DATE( " + DateSQL( Date() ) + " ), FIVALOR, 0 ) ) AS ATRASO" + ;
         " FROM JPFINAN " + ;
         " LEFT JOIN JPPEDIDO ON JPPEDIDO.IDPEDIDO = " + NumberSQL( nIdPedido ) + ;
         " WHERE FICADASTRO = JPPEDIDO.PDCADASTRO" + ;
         " AND FITIPLAN = '1' AND FIDATCAN IS NULL AND FIDATPAG IS NULL"
      :Execute()
      mAtraso := :Number( "ATRASO" )
      mAberto := :Number( "ABERTO" )
      :CloseRecordset()
   ENDWITH

   RETURN NIL


Isso virou isto no SQL

CREATE PROCEDURE `ze_FinanceiroAberto`(
   IN `nIdPedido` INT(11),
   IN `dData` DATE
)
BEGIN

DECLARE nAberto, nAtraso DECIMAL(16,2) DEFAULT 0;

IF COALESCE( nIdPedido, 0 ) <> 0 THEN

   SELECT SUM( FIVALOR ),
      SUM( IF( DATE( FIDATVEN ) < DATE( dData  ), FIVALOR, 0 ) )
      FROM JPFINAN
      LEFT JOIN JPPEDIDO ON JPPEDIDO.IDPEDIDO = nIdPedido
      WHERE FICADASTRO = JPPEDIDO.PDCADASTRO
         AND FITIPLAN = '1' AND FIDATCAN IS NULL AND FIDATPAG IS NULL
   INTO nAberto, nAtraso;

   SELECT COALESCE( nAberto, 0 ) AS ABERTO, COALESCE( nAtraso, 0 ) AS ATRASO;

END IF;

END


Nesse caso não mudou muito, porque ainda vai ter o recordset, apenas o comando vai ficar reduzido.

No Harbour vai ficar assim:

STATIC FUNCTION SituacaoFinanceiro( nIdPedido, mAberto, mAtraso )

   LOCAL cnSQL := ADOClass():new( AppConexao() )

   WITH OBJECT cnSQL
      :cSQL := "call ze_financeiroAberto( " + NumberSQL( nIdPedido ) + ", " + DateSQL( Date() ) + " )"
     :Execute()
      mAtraso := :Number( "ATRASO" )
      mAberto := :Number( "ABERTO" )
      :CloseRecordset()
   ENDWITH

   RETURN NIL
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 19 Ago 2021 23:10

Uma coisa importante sobre stored procedure:

Em certos casos, pode acontecer da variável conter nulo (Nil).
Via programa, com o meu uso, Nil em numérico é transformado pra ZERO.
Mas na stored procedure, pode acontecer de chegar o valor nulo, o que dá problema pra cálculos e outras coisas.

Por isso o teste com COALESCE(), que pega o primeiro valor não-nulo da sequência, evitando o erro de tipos incompatíveis.

E outra coisa importante: se der erro na stored procedure e/ou function... nem sempre temos a referência de onde aconteceu o erro.
Talvez por isso seja bom criar rotinas pequenas e funcionais.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 20 Ago 2021 11:04

Esta ficou interessante, pra duplicar um pedido.

Em Harbour
   WITH OBJECT cnSQL
      :cSQL := "SELECT JPPEDIDO.*, JPITPED.*" + ;
         " FROM JPPEDIDO" + ;
         " LEFT JOIN JPITPED ON JPITPED.IPPEDIDO = JPPEDIDO.IDPEDIDO" + ;
         " WHERE IDPEDIDO = " + NumberSQL( nIdPedido )
      :Execute()
      :QueryCreate()
      :QueryAdd( "PDCONF", "N" )
      :QueryAdd( "PDDATEMI", Date() )
      :QueryAdd( "PDINFINC", LogInfo() )
      IF AppEmpresaApelido() == "ADRIANA"
         :QueryAdd( "PDTRANSACAO", 998001 ) // NF PJ
      ELSE
         :QueryAdd( "PDTRANSACAO", :Number( "PDTRANSACAO" ) )
      ENDIF
      FOR EACH cField IN { "PDFILIAL", "PDCADASTRO", "PDVENDEDOR", "PDTRANSP", "PDFORPAG" }
         :QueryAdd( cField, StrZero( :Number( cField ), 6 ) )
      NEXT
      FOR EACH cField IN { "PDPEDCLI" }
         :QueryAdd( cField, StrZero( :Number( cField ), 10 ) )
      NEXT
      FOR EACH cField IN { "PDCONTATO", "PDEMAIL", "PDOBS", "PDLEIS" }
         :QueryAdd( cField, :String( cField ) )
      NEXT
      FOR EACH cField IN { "PDPERDES", "PDPERADI", "PDVALCUS", "PDVALPRO", "PDVALNOT", "PDVALFRE", ;
            "PDVALSEG", "PDVALOUT", "PDVALEXT", "PDVALDES", "PDVALADI", "PDVALADU", "PDVALIOF", "PDIIBAS", ;
            "PDIIVAL", "PDIPIBAS", "PDICMVAL", "PDFCPVAL", "PDSUBBAS", "PDSUBVAL", "PDDIFVALI", "PDDIFVALF", ;
            "PDISSBAS", "PDISSVAL", "PDIPIVAL", "PDICMBAS", "PDPISBAS", "PDPISVAL", "PDCOFBAS", "PDCOFVAL", ;
            "PDICSBAS", "PDICSVAL", "PDIMPVAL" }
         :QueryAdd( cField, :Number( cField ) )
      NEXT
      nIdPedido := :QueryExecuteInsert( "JPPEDIDO" )
      DO WHILE ! :Eof()
         :QueryCreate()
         :QueryAdd( "IPPEDIDO", StrZero( nIdPedido, 6 ) )
         :QueryAdd( "IPINFINC", LogInfo() )
         FOR EACH cField IN { "IPFILIAL", "IPPRODUTO", "IPTRIBUT" }
            :QueryAdd( cField, StrZero( :Number( cField ), 6 ) )
         NEXT
         FOR EACH cField IN { "IPPEDCOM", "IPCFOP", "IPLEIS", "IPORIGEM", "IPIPIICM", "IPIPICST", ;
               "IPIPIENQ", "IPICMCST", "IPDIFCAL", "IPPISCST", "IPPISENQ", "IPCOFCST", "IPCOFENQ" }
            :QueryAdd( cField, :String( cField ) )
         NEXT
         FOR EACH cField IN { "IPPRECUS", "IPPREPED", "IPQTDE", "IPVALCUS", "IPGARANTIA", "IPPRENOT", ;
               "IPVALADI", "IPVALFRE", "IPVALSEG", "IPVALOUT", "IPVALEXT", "IPVALADU", "IPVALIOF", "IPVALDES", ;
               "IPVALPRO", "IPVALNOT", "IPIIBAS", "IPIIALI", "IPIPIBAS", "IPIPIALI", "IPIPIVAL", "IPICMBAS", ;
               "IPICMALI", "IPICMRED", "IPICMVAL", "IPFCPALI", "IPFCPVAL", "IPICSBAS", "IPICSALI", "IPICSVAL", ;
               "IPSUBIVA", "IPSUBBAS", "IPSUBRED", "IPSUBALI", "IPSUBVAL", "IPDIFBAS", "IPDIFALIF", "IPDIFALIU", ;
               "IPDIFALII", "IPDIFVALI", "IPDIFVALF", "IPPISBAS", "IPPISALI", "IPPISVAL", "IPCOFBAS", "IPCOFALI", ;
               "IPCOFVAL", "IPISSBAS", "IPISSALI", "IPISSVAL", "IPIMPALI", "IPIMPVAL" }
            :QueryAdd( cField, :Number( cField ) )
         NEXT
         :QueryExecuteInsert( "JPITPED" )
         :MoveNext()
      ENDDO
      :CloseRecordset()
   ENDWITH


e em SQL

CREATE PROCEDURE ze_PedidoClonar( nIdPedido INT(11), dDate DATE, cLogInfo VARCHAR(100), cEmpresaApelido VARCHAR(50) )

BEGIN

DECLARE nNovoPedido INT(11);

INSERT INTO JPPEDIDO
   ( PDCONF, PDDATEMI, PDINFINC, PDTRANSACAO, PDFILIAL, PDCADASTRO,
   PDVENDEDOR, PDTRANSP, PDFORPAG, PDPEDCLI, PDCONTATO, PDEMAIL, PDOBS, PDLEIS,
   PDPERDES, PDPERADI, PDVALCUS, PDVALPRO, PDVALNOT, PDVALFRE,
   PDVALSEG, PDVALOUT, PDVALEXT, PDVALDES, PDVALADI, PDVALADU, PDVALIOF, PDIIBAS,
   PDIIVAL, PDIPIBAS, PDICMVAL, PDFCPVAL, PDSUBBAS, PDSUBVAL, PDDIFVALI, PDDIFVALF,
   PDISSBAS, PDISSVAL, PDIPIVAL, PDICMBAS, PDPISBAS, PDPISVAL, PDCOFBAS, PDCOFVAL,
   PDICSBAS, PDICSVAL, PDIMPVAL )
SELECT
   'N', dDate, cLogInfo, IF( cEmpresaApelido = 'ADRIANA', 998001, PDTRANSACAO ), PDFILIAL, PDCADASTRO,
   PDVENDEDOR, PDTRANSP, PDFORPAG, PDPEDCLI, PDCONTATO, PDEMAIL, PDOBS, PDLEIS,
   PDPERDES, PDPERADI, PDVALCUS, PDVALPRO, PDVALNOT, PDVALFRE,
   PDVALSEG, PDVALOUT, PDVALEXT, PDVALDES, PDVALADI, PDVALADU, PDVALIOF, PDIIBAS,
   PDIIVAL, PDIPIBAS, PDICMVAL, PDFCPVAL, PDSUBBAS, PDSUBVAL, PDDIFVALI, PDDIFVALF,
   PDISSBAS, PDISSVAL, PDIPIVAL, PDICMBAS, PDPISBAS, PDPISVAL, PDCOFBAS, PDCOFVAL,
   PDICSBAS, PDICSVAL, PDIMPVAL FROM JPPEDIDO WHERE IDPEDIDO = nIdPedido;

SELECT LAST_INSERT_ID() INTO nNovoPedido;

INSERT INTO JPITPED
   ( IPPEDIDO, IPINFINC, IPFILIAL, IPPRODUTO, IPTRIBUT,
     IPPEDCOM, IPCFOP, IPLEIS, IPORIGEM, IPIPIICM, IPIPICST,
     IPIPIENQ, IPICMCST, IPDIFCAL, IPPISCST, IPPISENQ, IPCOFCST, IPCOFENQ,
     IPPRECUS, IPPREPED, IPQTDE, IPVALCUS, IPGARANTIA, IPPRENOT,
     IPVALADI, IPVALFRE, IPVALSEG, IPVALOUT, IPVALEXT, IPVALADU, IPVALIOF, IPVALDES,
     IPVALPRO, IPVALNOT, IPIIBAS, IPIIALI, IPIPIBAS, IPIPIALI, IPIPIVAL, IPICMBAS,
     IPICMALI, IPICMRED, IPICMVAL, IPFCPALI, IPFCPVAL, IPICSBAS, IPICSALI, IPICSVAL,
     IPSUBIVA, IPSUBBAS, IPSUBRED, IPSUBALI, IPSUBVAL, IPDIFBAS, IPDIFALIF, IPDIFALIU,
     IPDIFALII, IPDIFVALI, IPDIFVALF, IPPISBAS, IPPISALI, IPPISVAL, IPCOFBAS, IPCOFALI,
     IPCOFVAL, IPISSBAS, IPISSALI, IPISSVAL, IPIMPALI, IPIMPVAL )
SELECT
     nNovoPedido, cLogInfo, IPFILIAL, IPPRODUTO, IPTRIBUT,
     IPPEDCOM, IPCFOP, IPLEIS, IPORIGEM, IPIPIICM, IPIPICST,
     IPIPIENQ, IPICMCST, IPDIFCAL, IPPISCST, IPPISENQ, IPCOFCST, IPCOFENQ,
     IPPRECUS, IPPREPED, IPQTDE, IPVALCUS, IPGARANTIA, IPPRENOT,
     IPVALADI, IPVALFRE, IPVALSEG, IPVALOUT, IPVALEXT, IPVALADU, IPVALIOF, IPVALDES,
     IPVALPRO, IPVALNOT, IPIIBAS, IPIIALI, IPIPIBAS, IPIPIALI, IPIPIVAL, IPICMBAS,
     IPICMALI, IPICMRED, IPICMVAL, IPFCPALI, IPFCPVAL, IPICSBAS, IPICSALI, IPICSVAL,
     IPSUBIVA, IPSUBBAS, IPSUBRED, IPSUBALI, IPSUBVAL, IPDIFBAS, IPDIFALIF, IPDIFALIU,
     IPDIFALII, IPDIFVALI, IPDIFVALF, IPPISBAS, IPPISALI, IPPISVAL, IPCOFBAS, IPCOFALI,
     IPCOFVAL, IPISSBAS, IPISSALI, IPISSVAL, IPIMPALI, IPIMPVAL
     FROM JPITPED WHERE IPPEDIDO = nIdPedido;

END


A rotina Harbour poderia ser trocada pra usar os mesmos comandos.
Mas com stored procedure, a informação pela rede vai ser mínima.
E dá até pra chamar direto no HEIDISQL apenas chamando a procedure.

Foi justamente assim que testei, digitando a chamada no HeidiSQL e repetindo algumas vezes.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 20 Ago 2021 11:13

rotina.png


Detalhe com o meu esquema do JPBINARY:

Isso está em um arquivo aqui na minha máquina chamado ze_pedidoduplica.sql

Ao fazer o update para o meu servidor da internet, vai ser instalado em TODOS os clientes assim que eles pedirem pra atualizar.

O aplicativo ainda não vai usar, porque não alterei o aplicativo ainda, mas vai estar pronto pra uso em tudo que é lugar, antes mesmo de alterar o aplicativo.

Muito doido isso do meu JPBINARY.
Nunca foi tão simples atualizar o aplicativo.

Ainda estou achando muito bom pra ser verdade... mas tá funcionando...
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 23 Ago 2021 08:37

Agora pegando a parte ruim do troço....

STORED PROCEDURE/FUNCTION são mais sensíveis a erro do que o uso normal.

Tem o caso de NULL, e outros casos mais, que precisam ser tratados.

E não tem mensagem amigável.

Reforçando uma coisa que eu já disse antes:

É fazer rotinas pequenas, pra fácil acompanhamento/manutenção, e testar muito cada uma.
E tentar prever o máximo possível de erros.

O tratamento de erros em PROCEDURE/FUNCTION é pelo tipo de erro.
Por exemplo: você pode criar um tratamento de erro do tipo "file not found", mas só vai servir pra esse tipo de erro, e só vai servir pra essa stored procedure.
Como é que vou fazer tratamento de erro pra erros que não sei ?
Pois é...
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 23 Ago 2021 18:45

Achei esta página interessante sobre tratar erros em STORED PROCEDURE

https://developpaper.com/detailed-explanation-of-error-handling-examples-of-mysql-stored-procedures/


DECLARE <action HANDLER> FOR <condition_value statement>;


Exemplos:

DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET deu_erro = 1;
DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT 'deu erro, mas só vai mostrar isto'
DECLARE CONTINUE HANDLER FOR NOT FOUND SET nao_achou = 1;
DECLARE CONTINUE HANDLER FOR 1062 SELECT 'chave duplicada';
DECLARE CONTINUE HANDLER FOR 1062
BEGIN
/* any */
END


Segundo o texto, o próprio MySQL vai decidir qual tratamento usar, considerando uma prioridade, o tratamento mais adequado para o tipo de erro.

É como ter o BEGIN SEQUENCE, mas definir um erro pra variável não existe, ALIAS não existe, campo não existe, etc.
Tratar os erros conhecidos, e deixar os desconhecidos pra outra rotina.
ISSO É POR PROCEDURE, POR ROTINA, não é geral.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 27 Ago 2021 15:05

Meu esquema atual de atualizações ficou falho.

Estou usando aquele JPBINARY, que contém tudo amontado.
Atualizei servidor internet, ele atualiza servidor cliente, ok, tudo ok.
Coloquei pra, ao atualizar o BINARY, se for procedure/function já cria no servidor local.
Tudo ok.

Mas....
E se for pra apagar?
Apesar de parecer relativamente simples, se apagar antes da hora pode fazer falta, e gerar erro.
Até pra renomear complicou, porque agora tem rotina antiga e nova, e mesmo que a nova trate isso, a antiga não trata.

Vou ter que revisar com cuidado antes que seja tarde.

Tirando isso, tá funcionando normalmente, atualizar rotinas sem precisar atualizar executável.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 27 Ago 2021 15:08

E separando rotinas em rotinas menores, pra ficar fácil de dar manutenção.
Por exemplo, uma rotina só pra calcular o IPI de um produto do pedido:

REATE PROCEDURE ze_PedidoCalculoIPI( nidItPed INT(11) )

THIS:BEGIN

DECLARE nIPIBas, nValPro, nIIVal, nIpiAli, nIpiVal DECIMAL(16,2) DEFAULT 0;

IF COALESCE( nIdItPed, 0 ) = 0 THEN
   LEAVE THIS;
END IF;

SELECT IPIPIALI, IPVALPRO, IPIIVAL
FROM JPITPED
WHERE IDITPED = nIdItPed
INTO nIpiAli, nValPro, nIIVal;

IF nIpiAli > 0 THEN
   SET nIpiBas = nValPro + nIIVal;
   SET nIpiVal = FLOOR( nIpiBas * nIpiAli ) / 100;
END IF;

UPDATE JPITPED
SET
IPIPIBAS = nIpiBas,
IPIPIVAL = nIpiVal
WHERE IDITPED = nIdItPed;

END


Não estou economizando fonte.
Apesar de poder fazer num único UPDATE/SELECT, criando variáveis pra facilitar manutenção.

Sinceramente.... sem as checagens de compilador, igual o Harbour tem, não estou me sentindo à vontade com tudo isso.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 28 Ago 2021 11:32

Fiz este teste hoje, talvez ajude a organizar as coisas, sem ter que ficar repetindo consultas.

CREATE PROCEDURE teste()

DECLARE A,B INT(11) DEFAULT 0;

CALL Teste2( A, B );
CALL Teste2( A, B );
CALL Teste2( A, B );

SELECT A, B;

END


CREATE PROCEDURE teste2( INOUT A INT(11), INOUT B INT(11) )

BEGIN

SET A = A + 1;
SET B = B + 2;

END


Baseado nisso, uma procedure de ler/salvar todas as variáveis chamando menores, uma de cálculo de ICMS, outra de IPI, etc. cada uma tratando suas próprias variáveis. As menores se limitariam a receber valores e fazer contas, enquanto a maior se limita a criar/salvar a parafernália toda.
Imagino que seja mais rápido do que ficar lendo/salvando em cada uma.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 28 Ago 2021 11:36

Seria o mesmo que conseguimos no Harbour usando variáveis por referência.
Cada rotina independente no que se refere a variáveis.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 28 Ago 2021 13:08

Comecei a alterar, mas parece que só vai complicar.
Muita variável, é dar chance a fazer coisa errada.
Continuar do jeito que eu tinha começado mesmo.

CREATE PROCEDURE ze_Pedidocalculo( nIdPedido INT(11) )

THIS:BEGIN

DECLARE nEnd INT(11) DEFAULT 0;
DECLARE nIdItPed INT(11) DEFAULT 0;
DECLARE curItPed CURSOR FOR
   SELECT idItPed
   FROM JPITPED
   WHERE IPPEDIDO = nIdPedido;
DECLARE CONTINUE HANDLER
   FOR NOT FOUND SET nEnd = 1;

IF COALESCE( nIdPedido, 0 ) = 0 THEN
   LEAVE THIS;
END IF;

/* CALL ze_PedidoCalculoRateioExtras( nIdPedido ); */
/* CALL ze_PedidoCalculoRateioOficial( nIdPedido ); */

OPEN curItPed;

Produtos:LOOP

   FETCH curItPed INTO nIdItPed;
   IF nEnd = 1 THEN
      LEAVE Produtos;
   END IF;
   /* CALL ze_PedidoCalculoTributacao( nIdItPed ); */
   /* CALL ze_PedidoCalculoII( nIdItPed ); */
   /* CALL ze_PedidoCalculoISS( nIdItPed ); */
   /* CALL ze_PedidoCalculoIPI( nIdItPed ); */
   /* CALL ze_PedidoCalculoICMS( nIdItPed ); */
   /* CALL ze_PedidoCalculoST( nIdItPed ); */
   /* CALL ze_PedidoCalculoFCP( nIdItPed ); */
   /* CALL ze_PedidoCalculoDIFAL( nIdItPed ); */
   /* CALL ze_PedidoCalculoPis( nIdItPed ); */
   /* CALL ze_PedidoCalculoCofins( nIdItPed ); */
   /* CALL ze_PedidoCalculoImportacao( nIdItPed ); */

END LOOP Produtos;

CLOSE curItPed;

/* CALL ze_PedidoCalculoTotais( nIdItPed ); */

END


Pra facilitar testes, vou desbloquear uma parte de cada vez, removendo o equivalente do aplicativo.
A somatória total vai ser a última a liberar, porque não adianta somar incompleto.
Independente disso, no aplicativo dá pra chamar cada rotina isoladamente pra teste.
Melhor acompanhar por linha/coluna do fonte Harbour, do que por um erro em stored procedure.

Ainda é novidade isso, ainda estou me adaptando ao que pode facilitar ou não.
A única coisa que dá pra dizer é que funciona, de um jeito ou de outro.

E... em stored procedure, precisa prever mais erros, porque certos ajustes que o aplicativo Harbour acaba fazendo não vão existir nas procedures.
Principalmente no que se refere a valores nulos, ou registros não encontrados.
Só lembrando: no Harbour/DBF em Eof() vém tudo com valores zerados, mas no MySQL vai ser NULL.
E operações com NULL geram NULL como resultado.

É questão de se acostumar, mas mexendo com fontes Harbour e SQL ao mesmo tempo, estou sempre deixando passar alguma coisa.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Rotina Harbour pra SQL

Mensagempor JoséQuintas » 28 Ago 2021 13:27

CREATE PROCEDURE ze_PedidoCalculoTributacao( nIdItPed INT(11) )

THIS:BEGIN

IF COALESCE( nIdItPed, 0 ) = 0 THEN
   LEAVE THIS;
END IF;

/* aplica regra de tributação */

   UPDATE JPITPED
   LEFT JOIN JPPEDIDO    ON IDPEDIDO = IPPEDIDO
   LEFT JOIN JPCADASTRO  ON IDCADASTRO = PDCADASTRO
   LEFT JOIN JPITEM      ON IDPRODUTO = IPPRODUTO
   LEFT JOIN JPTABUF     ON IDUF = CDUFENT
   LEFT JOIN JPTRANSACAO ON IDTRANSACAO = PDTRANSACAO
   LEFT JOIN JPIMPOSTO
   ON IMTRANSACAO = PDTRANSACAO
      AND IMTRIUF = UFTRIUF
      AND IMTRICAD = CDTRICAD
      AND IMTRIPRO = IETRIPRO
   LEFT JOIN JPTABIBPT ON IBCODIGO = JPITEM.IENCM
   SET
      IPCFOP    = COALESCE( IMCFOP, '' ),
      IPIIALI   = COALESCE( IMIIALI, 0 ),
      IPISSALI  = COALESCE( IMISSALI, 0 ),
      IPIPICST  = COALESCE( IMIPICST, '' ),
      IPIPIALI  = COALESCE( IMIPIALI, 0 ),
      IPIPIICM  = COALESCE( IMIPIICM, '' ),
      IPIPIENQ  = COALESCE( IMIPIENQ, '' ),
      IPICMCST  = COALESCE( IMICMCST, '' ),
      IPICMALI  = COALESCE( IMICMALI, 0 ),
      IPICMRED  = COALESCE( IMICMRED, 0 ),
      IPFCPALI  = COALESCE( IMFCPALI, 0 ),
      IPSUBALI  = COALESCE( IMSUBALI, 0 ),
      IPSUBIVA  = COALESCE( IMSUBIVA, 0 ),
      IPSUBRED  = COALESCE( IMSUBRED, 0 ),
      IPDIFCAL  = COALESCE( IMDIFCAL, '' ),
      IPDIFALII = COALESCE( IMDIFALII, 0 ),
      IPDIFALIU = COALESCE( IMDIFALIU, 0 ),
      IPDIFALIF = COALESCE( IMDIFALIF, 0 ),
      IPPISCST  = COALESCE( IMPISCST, 0 ),
      IPPISALI  = COALESCE( IMPISALI, 0 ),
      IPPISENQ  = COALESCE( IMPISENQ, '' ),
      IPCOFCST  = COALESCE( IMCOFCST, '' ),
      IPCOFALI  = COALESCE( IMCOFALI, 0 ),
      IPCOFENQ  = COALESCE( IMCOFENQ, '' ),
      IPICSALI  = COALESCE( IMICSALI, 0 ),
      IPLEIS    = COALESCE( IMLEIS, '' ),
      IPTRIBUT  = COALESCE( IDIMPOSTO, 0 )
   WHERE IDITPED = nidItPed AND IPTRIBUT <> 999999;

END


Essa rotina acima é a que aplica a regra de tributação.
Desse jeito, já grava a nova regra.
Se eu for criar variáveis... só vai complicar.
Por isso achei melhor continuar desse jeito mesmo.

Se fizer pra todos os produtos do pedido de uma vez, ou invididual, dá praticamente no mesmo.
Deixar assim mesmo, por produto, porque por enquanto nem sei dizer qual é melhor.
Se ficar mudando muito de idéia, vai acabar não saindo nada.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg, multithread, dbfcdx, ADO+MySql, PNotepad
"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 de usuário

JoséQuintas
Membro Master

Membro Master
 
Mensagens: 18125
Data de registro: 26 Fev 2007 11:59
Cidade/Estado: São Paulo-SP
Curtiu: 15 vezes
Mens.Curtidas: 1215 vezes

Próximo



Retornar para SQL

Quem está online

Usuários vendo este fórum: Nenhum usuário registrado online e 5 visitantes


Ola Amigo, espero que meu site e forum tem lhe beneficiado, com exemplos e dicas de programacao.
Entao divulgue o link da Doacao abaixo para seus amigos e redes sociais ou faça uma doacao para o site forum...
MUITO OBRIGADO PELA SUA DOACAO!
Faça uma doação para o forum
cron
v
Olá visitante, seja bem-vindo ao Fórum Clipper On Line!
Efetue o seu login ou faça o seu Registro