HMG + MYSQL store procedure

HMG en Español

Moderator: Rathinagiri

Post Reply
User avatar
edufloriv
Posts: 200
Joined: Thu Nov 08, 2012 3:42 am
DBs Used: DBF, MariaDB, MySQL, MSSQL, MariaDB
Location: PERU
Has thanked: 8 times
Been thanked: 13 times

HMG + MYSQL store procedure

Post 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.

Eduardo Flores Rivas


LIMA - PERU

User avatar
Claudio Ricardo
Posts: 181
Joined: Tue Oct 27, 2020 3:38 am
DBs Used: DBF, MySQL, MariaDB
Location: Bs. As. - Argentina
Has thanked: 46 times
Been thanked: 70 times

Post 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.
Corrige al sabio y lo harás más sabio, Corrige al necio y lo harás tu enemigo.
WhatsApp / Telegram: +54 911-63016162

jorge.posadas
Posts: 119
Joined: Mon May 19, 2014 7:43 pm
DBs Used: DBF, SQLite, MS-SQL, ACCESS, MariaDB (en proceso)
Location: Tizayuca, Hgo. México
Been thanked: 12 times
Contact:

Post 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
Cordialmente

POSADAS SOFTWARE
Jorge Posadas Ch.
Programador independiente
Tizayuca, Hgo.
M é x i c o .
Movil +52 55 2038 5338
SKYPE: jorge.posadasch
Email: posadas_software@outlook.com

User avatar
serge_girard
Posts: 2743
Joined: Sun Nov 25, 2012 2:44 pm
DBs Used: 1 MySQL - MariaDB
2 DBF
Location: Belgium
Has thanked: 1019 times
Been thanked: 186 times
Contact:

Post 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

User avatar
edufloriv
Posts: 200
Joined: Thu Nov 08, 2012 3:42 am
DBs Used: DBF, MariaDB, MySQL, MSSQL, MariaDB
Location: PERU
Has thanked: 8 times
Been thanked: 13 times

Post 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.

Eduardo Flores Rivas


LIMA - PERU

User avatar
Ismach
Posts: 145
Joined: Wed Nov 28, 2012 5:55 pm
DBs Used: DBF, mySQL, Mariadb, postgreSQL, Oracle, Db2, Interbase, Firebird, and SQLite
Location: Buenos Aires - Argentina
Been thanked: 16 times

Post 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();

User avatar
salamandra
Posts: 301
Joined: Thu Jul 31, 2008 8:33 pm
DBs Used: DBF, MySQL, SQL
Location: Brazil
Has thanked: 203 times
Been thanked: 17 times

Post 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
There is one time in which is crucial awakening. That time is now. ( Buddha )

jparada
Posts: 356
Joined: Fri Jan 23, 2009 5:18 pm
Has thanked: 4 times
Been thanked: 11 times

Post 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

Post Reply