Llamada a SP's de SQL Server con parámetros OUTPUT

700 views
Skip to first unread message

Miguel Angel

unread,
Jul 16, 2012, 5:17:43 AM7/16/12
to publice...@googlegroups.com
Hola.

Primero quiero presentarme, soy nuevo en Visual FoxPro y esta es mi primera pregunta sobre un tema que empieza a desesperarme.

Quiero desarrollar una aplicación con capas para acceder a MsSQL Server, con código en Visual FoxPro. El caso es que la capa ya la tengo terminada y funciona correctamente, pero hay un detalle que no consigo terminar. Quiero que pase a través de la capa las llamadas a SP's que retornen parámetros OUTPUT.

Funciona de forma directa si llamo directamente a través de SQLEXEC pero no si lo hago a través de mi clase. Buscando en internet encontré el trabajo "Visual FoxPro 9.0 y SqlServer 2005" de Miguel Antúnez Camones que parece indicar que esto es posible. Si estáis interesados podéis consultar:
- Código:  Fichero "execu.prg"   Método "ExecSp" (página 15 del pdf)
- Llamada: Clase "Cliente"   Método "inserta"   (página 18 del pdf)

¿Alguien tiene el ejemplo de este código completo o sabe donde se puede descargar?

Muchas gracias y recibir un cordial saludo.

mpulla

unread,
Jul 16, 2012, 8:18:58 AM7/16/12
to publice...@googlegroups.com

Hola Miguel.

LOCAL lcSql As String, liKeyNext As Integer, lcErrMsg As String
liKeyNext = 0

lcSql = 'spC_TurnoNavigation ?tiMovTip, ?tiNumero, ?@liKeyNext'
IF SQLEXEC(This.iOdbchandle, lcSql) < 0
     AERROR(laError)
     lcErrMsg = laError(2)
ENDIF

El SP me retorna un numeroy la almaceno en la variable liKeyNext.

Saludos.
Mauricio


Miguel Antúnez

unread,
Jul 16, 2012, 8:43:02 AM7/16/12
to publice...@googlegroups.com
Con la @ puedes recibir los parámetros, para eso tus variables en el sql server tiene que ser de tipo output,
anexo un ejemplo sencillo.

create procedure operaciones

@uno int,

@dos int,

@suma int output,

@resta int output,

@multiplica int output

as

set @suma= @uno+@dos

set @resta= @uno-@dos

set @multiplica= @uno*@dos

go

grant execute on operaciones to public

 

------------------------VFP

m.v1=10

m.v2=5

m.v3=0

m.v5=0

?cone.execsp("operaciones","?m.v1,?m.v2,?@m.v3,?@m.v4,?@m.v5")

?m.v3

?m.v4

?m.v5


 

2012/7/16 mpulla <jmaur...@yahoo.es>



--
Miguel Angel Antúnez Camones
mant...@gmail.com


Miguel Angel

unread,
Jul 16, 2012, 10:58:23 AM7/16/12
to publice...@googlegroups.com
Hola.

Gracias por la respuesta.
Así si he logrado que me funcione, el problema es que no quiero permitir llamadas directas y quiero que todas pasen a través de una capa de conexión con la BBDD. Algo similar a lo que Miguel Antúnez comenta en su artículo. De hecho estoy por rendirme y hacer las llamadas de forma directa, en los (espero) pocos sitios que se haga que estén muy bien localizados e identificados.

En la réplica a la siguiente respuesta, pongo un ejemplo de código (por no repetirla :-).

Saludos cordiales.

Miguel Angel

unread,
Jul 16, 2012, 11:18:22 AM7/16/12
to publice...@googlegroups.com
Reitero las gracias.
Repito el código por si es de utilidad a futuras búsquedas de este caso (simplificado para el caso concreto).

El código que tengo es:


LOCAL retexec AS Integer
LOCAL retsp AS Integer
LOCAL s1 AS Integer
LOCAL s2 AS Integer
LOCAL pout AS Integer
m.s1=11
m.s2=22
m.pout=0

TEXT TO parametros_sp NOSHOW
     ?m.s1, ?m.s2, ?@m.pout
ENDTEXT
LOCAL parametros_sp2 AS String
parametros_sp2="?m.s1, ?m.s2, ?@m.pout"


* NO FUNCIONAN:
* Pide valores para m.s1, m.s2, m.pout (en el método ODBC_EjecutaSP).
* En la llamada al SQLEXEC lanza el error "Variable 'POUT' is not found".
valor=bd.ODBC_EjecutaSP("pru_pout_return", parametros_sp, @v_return)
*valor=bd.ODBC_EjecutaSP("pru_pout_return", parametros_sp2, @v_return)

* FUNCIONAN:
* No pide valores.
*valor=SQLEXEC(bd.Conexion(), "{?@m.v_return = Call pru_pout_return(" + parametros_sp + ")}")
*valor=SQLEXEC(bd.Conexion(), "{?@m.v_return = Call pru_pout_return(" + parametros_sp2 + ")}")

? "pru_pout_return) output=",m.pout," v_return=",m.v_return," valor=",m.valor
? "."

-------------------------------------------------------------------

FUNCTION ODBC_EjecutaSP(c_nombreSP AS String, c_parametros AS String, c_valReturn AS Integer)
    LOCAL retorno AS Logical
    LOCAL retexec AS Integer
    LOCAL sql_sp AS String
    LOCAL retsp AS Integer
    retsp=0

    sql_sp= "{?@m.retsp = Call "+c_nombreSP+"( "+c_parametros+" )}"
    retexec=SQLEXEC(this.objConexion, sql_sp)
    IF retexec>=1
        c_valReturn=m.retsp
        retorno=.T.
    ELSE
        retorno=.F.
    ENDIF

    RETURN retorno
ENDFUNC

-------------------------------------------------------------------

ALTER PROCEDURE [dbo].[pru_pout_return]
    @p1 INT,
    @p2 INT,
    @pout INT OUTPUT
AS
BEGIN
    SET @pout=@p1+@p2+2   
    INSERT INTO pru_nada (nombre,otro) VALUES ('desde [pru_pout_return] VFP1',@pout)
    RETURN @pout
END


El error se da porque las variables pasadas (m.s1, m.s2, m.pout) en los parámetros están en ámbitos de visibilidad diferentes. ¿Hay alguna forma de solucionar esto?.

Como comentaba a "mpulla" estaba por dejarlo y obligar a que los sitios donde se llame directamente sean pocos y muy bien localizados.

Muchas gracias (de nuevo)
Saludos cordiales.

mpulla

unread,
Jul 16, 2012, 1:14:36 PM7/16/12
to publice...@googlegroups.com

Hola.

Se me ocurre crear una variable publica para almacenar el valor, prueba esto

Saludos.
Mauricio

LOCAL retexec AS Integer
LOCAL retsp AS Integer
LOCAL s1 AS Integer
LOCAL s2 AS Integer
PUBLIC pout AS Integer
m.s1=11
m.s2=22
m.pout=0 

LOCAL parametros_sp AS String
parametros_sp = Transform( m.s1) + [, ] + Transform(m.s2) + [, ?@m.pout] 
 
bd.ODBC_EjecutaSP("pru_pout_return", parametros_sp)
Release pout
------------------------------------------------------------------

FUNCTION ODBC_EjecutaSP(c_nombreSP AS String, c_parametros AS String, c_valReturn AS Integer)
    ** Estarías ejecutando esto "pru_pout_return 11, 22, ?@m.pout"
       **ya no necesitarías la variable de retorno pues es publica 
    IF SQLEXEC(this.objConexion,  c_nombreSP " " + c_parametros) < 0
           =messagebox('Error') 
    ENDIF

Victor Espina

unread,
Jul 16, 2012, 3:46:53 PM7/16/12
to publice...@googlegroups.com
Miguel, la forma correcta de invocar un storedprocedure que contiene parametros tipo OUTPUT es la siguiente:

PRIVATE p1, p2, psalida
SQLEXEC(nConn,"EXEC nombre_sp ?p1, ?p2, ?@psalida")
?psalida

es IMPORTANTE que los parametros se declaren como PRIVATE y no como LOCAL.  Ahora, la sintaxis para invocar un SP que devuelve un valor mediante RETURN, es la siguiente:

SQLEXEC(nConn,"{?@nResult = nombre_sp(?p1, ?p2, ?@psalida)}")


Saludos

Victor Espina

Victor Espina

unread,
Jul 16, 2012, 3:47:57 PM7/16/12
to publice...@googlegroups.com
Corrijo la sintaxis para invocar un SP que devuelve un valor por RETURN:

SQLEXEC(nConn,"{?@nResult = CALL nombre_sp(?p1, ?p2, ?@psalida)}")

Saludos

Victor Espina

Miguel Angel

unread,
Jul 17, 2012, 3:13:52 AM7/17/12
to publice...@googlegroups.com
Hola.

Funciona. Muchas gracias a todos, la solución es la que comenta Victor Espinosa (muchas gracias).

Pongo el ejemplo que funciona para futuras referencias (he eliminado lo que no importa en este caso).

v_return=0
PRIVATE n1 AS Integer        && IMPORTANTE: ¡Como comenta Victor Espina, debe ser PRIVATE!
PRIVATE n2 AS Integer        && IMPORTANTE: ¡Como comenta Victor Espina, debe ser PRIVATE!
PRIVATE pout AS Integer     && IMPORTANTE: ¡Como comenta Victor Espina, debe ser PRIVATE!
PRIVATE pout2 AS Integer   && IMPORTANTE: ¡Como comenta Victor Espina, debe ser PRIVATE!
m.n1=111
m.n2=222
m.pout=0
m.pout2=0
LOCAL parametros2_sp2 AS String
parametros2_sp2 = "?m.n1, ?m.n2, ?@m.pout, ?@m.pout2"
TEXT TO parametros2_sp NOSHOW
    ?m.n1, ?m.n2, ?@m.pout, ?@m.pout2
ENDTEXT
valor=bd.ODBC_EjecutaSP("pru_pout_pout2_return", parametros2_sp,@v_return)
? "f1) pout=",m.pout," pout2=",m.pout2," v_return=",m.v_return," valor=",m.valor
valor=bd.ODBC_EjecutaSP("pru_pout_pout2_return", parametros2_sp2, @v_return)
? "f2) pout=",m.pout," pout2=",m.pout2," v_return=",m.v_return," valor=",m.valor


-------------------------------------------------------------------

FUNCTION ODBC_EjecutaSP(c_nombreSP AS String, c_parametros AS String, c_valReturn AS Integer)
    LOCAL retorno AS Logical
    LOCAL retexec AS Integer
    LOCAL sql_sp AS String
    LOCAL retsp AS Integer
    retsp=0

    sql_sp= "{?@m.retsp = Call "+c_nombreSP+"( "+c_parametros+" )}"
    retexec=SQLEXEC(this.objConexion, sql_sp)
    IF retexec>=1
        c_valReturn=m.retsp
        retorno=.T.
    ELSE
        retorno=.F.
    ENDIF

    RETURN retorno
ENDFUNC

-------------------------------------------------------------------

CREATE PROCEDURE [dbo].[pru_pout_pout2_return]
    @p1 INT,
    @p2 INT,
    @pout INT OUTPUT,
    @pout2 INT OUTPUT
AS
BEGIN
    SET @pout=@p1+@p2+2
    SET @pout2=@p1+@p2+20
    INSERT INTO pru_nada (nombre,otro) VALUES ('desde [pru_pout_pout2_return] VFP1',@pout2)
    RETURN 123
END

Reply all
Reply to author
Forward
0 new messages