Page 1 of 1

HMG + MYSQL store procedure

Posted: Sun May 02, 2021 6:15 pm
by edufloriv
Saludos amigos,

Como ya había comentado antes estoy realizando un proyecto de HMG + MySQL y estoy implementando algunas store procedures del lado del servidor MySQL.

Tengo ya trabajando estas llamadas:

Code: Select all

*>----------------------------------------------------------------------<*
*>----------------------------------------------------------------------<*
*>----------------------------------------------------------------------<*

FUNC VentaGrabSetNumDoc( nOperTipo,nOperVent )

LOCAL qCorre , oCorre
LOCAL lSeted := .F.

   DO CASE
      CASE nOperTipo = 1
         qCorre := "CALL sp_correnotaventa("+INTSTR(SIS_LOCAL)+','+VALSTR(nOperVent)+")"
      CASE nOperTipo = 2
         qCorre := "CALL sp_correboleta("+INTSTR(SIS_LOCAL)+','+VALSTR(nOperVent)+")"
      CASE nOperTipo = 3
         qCorre := "CALL sp_correfactura("+INTSTR(SIS_LOCAL)+','+VALSTR(nOperVent)+")"
   ENDCASE

   oCorre := oServer:Query( qCorre )
   IF ! oCorre:NetErr()
      lSeted := .T.
   ELSE
      MsgStop ( oCorre:Error() )
      nQueryError++
   ENDIF
   oCorre:Destroy()

RETURN( lSeted )
Ahora se me ha presentado la necesidad de hacer una store procedure que DEVUELVE una cadena de texto y tengo la duda de como RECIBO esa cadena dentro de HMG. He revisado los métodos de oServer:Query() y solo recibe registros a través de :GetRow() y :fieldGet() requiere el nombre de un CAMPO, por lo que aquí me he perdido, ya que lo que devuelve la store procedure es una cadena de texto. Agredeceré la ayuda de aquellos que ya vienen trabajando con HMG + MySQL.

Gracias anticipadas y reciban un cordial saludo.

Re: HMG + MYSQL store procedure

Posted: Sun May 02, 2021 7:11 pm
by Claudio Ricardo
Hola... Tal vez creando una tabla a tal fin y guardando la cadena de texto en un campo de esa tabla,
luego se puede leer/escribir/borrar ese campo desde cualquier pc terminal con acceso a la DB.

Re: HMG + MYSQL store procedure

Posted: Sun May 02, 2021 9:13 pm
by jorge.posadas
Eduardo

EN MS-SQL se concatena campos y se le da un nombre a esa concatenación, ejemplo:

SELECT USER_ID, FIRST_NAME+' '+LAST_NAME AS [FULL_NAME] FROM USERS WHERE USER_ID IN (34510997, 45890)

Y el resultado se vería así:

USER_ID FULL_NAME
--------------------------- --------------------------------------------------------
34510997 Leonardo Ramirez
45890 Norma Angelica Miranda

Re: HMG + MYSQL store procedure

Posted: Mon May 03, 2021 6:06 am
by serge_girard
Eduardo,

I don't think CALL proc will work...! You will have to change it in some SELECT as Jorge POSADAS mentioned above.
I once tried (long ago but cannot remember why!)

Serge

Re: HMG + MYSQL store procedure

Posted: Tue May 11, 2021 6:13 pm
by edufloriv
Saludos amigos,

Lo que sucede es que el store procedure es bastante complejo, es imposible reducirlo a solo un select:

Code: Select all

CREATE PROCEDURE `sp_descargalotecualquiera`(IN localid INT,IN articuloid INT,IN cuantas INT,IN modulo CHAR(4),IN operacion INT)
BEGIN

DECLARE loteslista varchar(300) DEFAULT "" ;
DECLARE descargas int DEFAULT 0 ;
DECLARE descargar int DEFAULT cuantas ;
DECLARE intentos  int DEFAULT 0 ;
DECLARE rlocked   int DEFAULT 0 ;
DECLARE finished  int DEFAULT 0 ;
DECLARE lote_id   char(20) ;
DECLARE lote_vc   char(8) ;
DECLARE lote_un   int     ;
DECLARE arti_fr   int     ;
DECLARE arti_cs   numeric(12,2) ;
DECLARE arti_lk   tinyint ;
DECLARE arti_st   int  ;
DECLARE curLotes CURSOR FOR
SELECT stock_lote,stock_vence,stock_unids
FROM articulos_stocks
WHERE stock_local_id = localid AND stock_articulo_id = articuloid AND stock_unids > 0 
ORDER BY stock_vence ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1 ;

lock_arti: loop
	select articulo_fracciones,articulo_costo_promed,articulo_rlock INTO arti_fr,arti_cs,arti_lk
    from articulos
    where articulo_id = articuloid ;

    if arti_lk = 0 then
		update articulos set articulo_rlock = 1 where articulo_id = articuloid ;
        set rlocked = 1 ;
        leave lock_arti ;
	end if ;

	set intentos = intentos + 1 ;

    if intentos > 5000 then
		leave lock_arti ;
    end if ;
end loop lock_arti ;

if rlocked = 1 then

	OPEN curLotes ;
	getLotes: LOOP

		SELECT sum(stock_unids) as stock_total INTO arti_st
        FROM articulos_stocks
		WHERE stock_local_id = localid AND stock_articulo_id = articuloid AND stock_unids > 0 ;

		FETCH curLotes INTO lote_id,lote_vc,lote_un ;

		IF finished = 1 THEN 
			LEAVE getLotes ;
		END IF;

        IF lote_un >= descargar THEN

			UPDATE articulos_stocks SET stock_unids = stock_unids - descargar WHERE stock_local_id = localid AND stock_articulo_id = articuloid AND stock_lote = lote_id ;

            INSERT INTO kardex (kdx_local_id,kdx_arti_id,kdx_fecha,kdx_tipo,kdx_oper_mod,kdx_oper_id,kdx_arti_frac,kdx_stock_ant,kdx_stock_mov,kdx_stock_sld    ,kdx_costo_prm,kdx_lote,kdx_vence)
                         VALUES( localid    ,articuloid ,NOW()    ,"-"     ,modulo      ,operacion  ,arti_fr      ,arti_st      ,descargar    ,arti_st-descargar,arti_cs      ,lote_id ,lote_vc  ) ;

			SET descargas = descargas + descargar ;
			SET loteslista = CONCAT( loteslista,"(",lote_id,",",lote_vc,",",descargar,")" );

	    ELSE

			UPDATE articulos_stocks SET stock_unids = 0 WHERE stock_local_id = localid AND stock_articulo_id = articuloid AND stock_lote = lote_id ;

            INSERT INTO kardex (kdx_local_id,kdx_arti_id,kdx_fecha,kdx_tipo,kdx_oper_mod,kdx_oper_id,kdx_arti_frac,kdx_stock_ant,kdx_stock_mov,kdx_stock_sld    ,kdx_costo_prm,kdx_lote,kdx_vence)
                         VALUES( localid    ,articuloid ,NOW()    ,"-"     ,modulo      ,operacion  ,arti_fr      ,arti_st      ,lote_un      ,arti_st-lote_un  ,arti_cs      ,lote_id ,lote_vc  ) ;

			SET descargas = descargas + lote_un ;
			SET loteslista = CONCAT( loteslista,"(",lote_id,",",lote_vc,",",lote_un,")" );

        END IF ;

        IF descargas = cuantas THEN
			LEAVE getLotes ;
	    ELSE
			SET descargar = descargar - descargas ;
        END IF ;

	END LOOP getLotes ;
	CLOSE curLotes ;

end if ;

insert into descargas values(modulo,operacion,loteslista) ;
update articulos set articulo_rlock = 0 where articulo_id = articuloid ;

END
Así que como ven estoy usando la solución que me planteo Claudio Ricardo (crear una tabla en el lado de servidor que sirva de puente de resultados) y funciona perfecto.

Aprovechando el post, si se fijan en el inicio de la store procedure hago un lock a un registro, y he puesto 5000 intentos, alguien sabe si esta cantidad va bien o es muy bajo. Lo pregunto porque es un poco dificil testear concurrencia con un host remoto.

Cordiales saludos a todos.

Re: HMG + MYSQL store procedure

Posted: Tue May 11, 2021 8:20 pm
by Ismach
Yo diria que al necesitar devolver un resultado, deja de ser un procedure para llamarse FUNCION MYSQL y lo crearia como tal
para mariadb -> https://mariadb.com/kb/en/create-function/
y para mysql -> https://dev.mysql.com/doc/refman/8.0/en ... edure.html

para invocarla se hace con un select

SELECT MI_FUNCION();

Re: HMG + MYSQL store procedure

Posted: Tue May 11, 2021 11:17 pm
by salamandra
Hola @edufloriv,

edufloriv wrote: Tue May 11, 2021 6:13 pm Saludos amigos,

Lo que sucede es que el store procedure es bastante complejo, es imposible reducirlo a solo un select:

Code: Select all

CREATE PROCEDURE `sp_descargalotecualquiera`(IN localid INT,IN articuloid INT,IN cuantas INT,IN modulo CHAR(4),IN operacion INT)
BEGIN

DECLARE loteslista varchar(300) DEFAULT "" ;
DECLARE descargas int DEFAULT 0 ;
DECLARE descargar int DEFAULT cuantas ;
DECLARE intentos  int DEFAULT 0 ;
DECLARE rlocked   int DEFAULT 0 ;
DECLARE finished  int DEFAULT 0 ;
DECLARE lote_id   char(20) ;
DECLARE lote_vc   char(8) ;
DECLARE lote_un   int     ;
DECLARE arti_fr   int     ;
DECLARE arti_cs   numeric(12,2) ;
DECLARE arti_lk   tinyint ;
DECLARE arti_st   int  ;
DECLARE curLotes CURSOR FOR
SELECT stock_lote,stock_vence,stock_unids
FROM articulos_stocks
WHERE stock_local_id = localid AND stock_articulo_id = articuloid AND stock_unids > 0 
ORDER BY stock_vence ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1 ;

lock_arti: loop
	select articulo_fracciones,articulo_costo_promed,articulo_rlock INTO arti_fr,arti_cs,arti_lk
    from articulos
    where articulo_id = articuloid ;

    if arti_lk = 0 then
		update articulos set articulo_rlock = 1 where articulo_id = articuloid ;
        set rlocked = 1 ;
        leave lock_arti ;
	end if ;

	set intentos = intentos + 1 ;

    if intentos > 5000 then
		leave lock_arti ;
    end if ;
end loop lock_arti ;

if rlocked = 1 then

	OPEN curLotes ;
	getLotes: LOOP

		SELECT sum(stock_unids) as stock_total INTO arti_st
        FROM articulos_stocks
		WHERE stock_local_id = localid AND stock_articulo_id = articuloid AND stock_unids > 0 ;

		FETCH curLotes INTO lote_id,lote_vc,lote_un ;

		IF finished = 1 THEN 
			LEAVE getLotes ;
		END IF;

        IF lote_un >= descargar THEN

			UPDATE articulos_stocks SET stock_unids = stock_unids - descargar WHERE stock_local_id = localid AND stock_articulo_id = articuloid AND stock_lote = lote_id ;

            INSERT INTO kardex (kdx_local_id,kdx_arti_id,kdx_fecha,kdx_tipo,kdx_oper_mod,kdx_oper_id,kdx_arti_frac,kdx_stock_ant,kdx_stock_mov,kdx_stock_sld    ,kdx_costo_prm,kdx_lote,kdx_vence)
                         VALUES( localid    ,articuloid ,NOW()    ,"-"     ,modulo      ,operacion  ,arti_fr      ,arti_st      ,descargar    ,arti_st-descargar,arti_cs      ,lote_id ,lote_vc  ) ;

			SET descargas = descargas + descargar ;
			SET loteslista = CONCAT( loteslista,"(",lote_id,",",lote_vc,",",descargar,")" );

	    ELSE

			UPDATE articulos_stocks SET stock_unids = 0 WHERE stock_local_id = localid AND stock_articulo_id = articuloid AND stock_lote = lote_id ;

            INSERT INTO kardex (kdx_local_id,kdx_arti_id,kdx_fecha,kdx_tipo,kdx_oper_mod,kdx_oper_id,kdx_arti_frac,kdx_stock_ant,kdx_stock_mov,kdx_stock_sld    ,kdx_costo_prm,kdx_lote,kdx_vence)
                         VALUES( localid    ,articuloid ,NOW()    ,"-"     ,modulo      ,operacion  ,arti_fr      ,arti_st      ,lote_un      ,arti_st-lote_un  ,arti_cs      ,lote_id ,lote_vc  ) ;

			SET descargas = descargas + lote_un ;
			SET loteslista = CONCAT( loteslista,"(",lote_id,",",lote_vc,",",lote_un,")" );

        END IF ;

        IF descargas = cuantas THEN
			LEAVE getLotes ;
	    ELSE
			SET descargar = descargar - descargas ;
        END IF ;

	END LOOP getLotes ;
	CLOSE curLotes ;

end if ;

insert into descargas values(modulo,operacion,loteslista) ;
update articulos set articulo_rlock = 0 where articulo_id = articuloid ;

END
Así que como ven estoy usando la solución que me planteo Claudio Ricardo (crear una tabla en el lado de servidor que sirva de puente de resultados) y funciona perfecto.

Aprovechando el post, si se fijan en el inicio de la store procedure hago un lock a un registro, y he puesto 5000 intentos, alguien sabe si esta cantidad va bien o es muy bajo. Lo pregunto porque es un poco dificil testear concurrencia con un host remoto.

Cordiales saludos a todos.
Y no solo es complejo como que un número alto de stored procedures degrada la performance del servidor...

Salamandra, Brazil

Re: HMG + MYSQL store procedure

Posted: Wed May 12, 2021 4:50 pm
by jparada
Hola Eduardo,

Cuando requiero tomar un único valor que devuelve una query (e imagino que será lo mismo con un SP) utilizo algo como:

Code: Select all

sqlCommand1 := "SELECT COUNT(*) FROM ..."

totalCtes := Ado_value( sqlCommand1 )

Function ADO_Value(cStr)
  Local oRs, xRet	
  oRs := oConn:Execute(cStr)
  If oRs:Eof
    xRet := ADO_FIELDSTRUCT( oRs, 0 )[5]
  Else
    xRet := oRs:Fields[0]:Value
  Endif
  oRs:Close()
Return xRet
Yo utilizo MS-SQL Server, puedes intentar y ver si te funciona así o te da la idea de algo similar.

Saludos,
Javier