HOWTO. ADO, SQL Server, Procedimientos Almacenados

HOWTO. ADO, SQL Server, Procedimientos Almacenados

Postby thefull » Thu Nov 27, 2008 4:10 pm

ADO, SQL Server, Procedimientos Almacenados y otras historias....

En un traspaso de datos desde DBF a SQL Server, quizás nos veamos en la necesidad
de llamar a un procedimiento almacenado, donde necesitamos enviar parámetros y
además necesitamos obtener datos que se modifican dentro del procedimiento almacenado.

Para ello, me he creado un par de clases, partiendo de las de Jose Luis Capel,
que nos permitirá llamar a un procedimiento almacenado, pasando valores y
obteniendo valores.

Por ejemplo algo como esto en el procedimiento almacenado ;
Code: Select all  Expand view
ALTER PROCEDURE [dbo].[SP_CREAR_EJEMPO]
  @pCODIGO            VARCHAR(3),
  @pPRECIO            INT,
  @pDESCRIPCION       VARCHAR(40),
  @pID                INT output



El ejemplo para llamar a un procedimiento es el siguiente;

Creamos el procedimiento almacenado llamado SP_CREAR_EJEMPO a traves de la clase TAdoStoreProc.
Nota: oConnection es la clase TAdConnect de Jose Luis Capel.

o
Code: Select all  Expand view
Cmd := TAdoStoreProc():New( "SP_CREAR_EJEMPO", oConnection )
   oAdoCmd := oCmd:GetADOCommand() // Obtemos el objeto ADO Command.


A partir de este momento, solamente vamos a alimentar a nuestras variables
del procedimiento almacenado;

o
Code: Select all  Expand view
ADOCmd:Parameters:Item('@CODIGO'):Value  := '001'
  oADOCmd:Parameters:Item('@pPRECIO'):Value :=  100.56
  oADOCmd:Parameters:Item('@pDESCRIPCION'):Value := 'Producto varios'

  oAdoCmd:Execute()

Ahora imaginemos que en el procedimiento Almacenado tenemos una variable
llamada por ejemlo @pID, que no es más que un Identity del último INSERT, que
nos servirá para hacer SELECT hacia otra tablas relacionadas, que nos a creado
el SP( Store Procedure ), por poner un ejemplo;

n
Code: Select all  Expand view
ID := oADOCmd:Parameters:Item('@pID'):Value // Obtenemos el ID insertado

cSql := [ SELECT * FROM TABLA WHERE ID_MASTER = ] + str( nID )

Este es un simple ejemplo de lo que podría suceder.*( Basado en hechos reales ;-) )

Bueno, ahora pondré el código fuente

Code: Select all  Expand view
#define adCmdStoredProc 4
---------------------------------------------------------------------
//---------------------------------------------------------------------
CLASS tAdoCommand
    DATA oCmd          //Objeto ADO Command
    DATA oParameters
    DATA oError

    METHOD New( oConnection ) CONSTRUCTOR
    METHOD Type( nType ) INLINE ::oCmd:CommandType := nType
    METHOD Text( cText ) INLINE ::oCmd:CommandText := cText
    METHOD GetADOCommand()  INLINE ::oCmd
END CLASS

METHOD New( oConnection ) CLASS tAdoCommand

       LOCAL oHErr

       ::oError := TAdoError():New()

       TRY
          ::oCmd := CreateObject("ADODB.Command")
       CATCH oHErr
          ::oError:Connection( oConnection )
          Throw( oHErr )
       END

       ::oCmd:ActiveConnection := oConnection:oConnection

RETURN Self



//---------------------------------------------------------------------
CLASS TAdoStoreProc FROM TAdoCommand
      METHOD New( cText, oConnection ) CONSTRUCTOR
      METHOD Execute()
END CLASS

METHOD New( cText, oConnection ) CLASS tAdoStoreProc
  Local oHErr

  try
    Super:New( oConnection )
    ::Type( adCmdStoredProc )
    ::Text( cText )
    ::oCmd:Parameters:Refresh()
  catch oHErr
    ::oError:Connection( oConnection )
    Throw( oHErr )
   end

RETURN Self

METHOD Execute( ) CLASS tAdoStoreProc
       LOCAL oError, cError, oErr
       LOCAL lResult

       lResult := .T.

       TRY
         ::oCmd:Execute()
       CATCH oErr
          ::oError:Connection( ::oConnection )
          lResult := .F.
          Throw( oErr )
       END

RETURN lResult

//---------------------------------------------------------------------
CLASS TAdoError
      DATA aErrors
      DATA oErrorDB
      METHOD New()
      METHOD Clean() INLINE ::aErrors := {}
      METHOD Connection( oConnection )
      METHOD ShowErrors()
END CLASS

METHOD New( lCreateError ) CLASS TAdoError

   ::aErrors := {}
   /* TODO: Pendiente de I+D
   if lCreateError
      ::oErrorDB := CreateObject( "ADODB.Error")
   endif
   */
RETURN Self

METHOD Connection( oConnection ) CLASS TAdoError

    Local oError, cError := ""

    ::Clean()

    For Each oError In oConnection:Errors
        cError := "Error #" + cValtoChar( oError:Number ) + CRLF +;
                  "   "  + oError:Description + CRLF +;
                  "   (Source: " + oError:Source + ")" + CRLF +;
                  "   (SQL State: " + oError:SQLState + ")" + CRLF +;
                  "   (NativeError: " + cValToChar( oError:NativeError ) + ")"
        AADD( ::aErrors, cError )
    Next

RETURN NIL

METHOD ShowErrors( ) CLASS TAdoError
    Local cError := ""

    For Each cError In ::aErrors
        MsgStop( cError )
    next

RETURN NIL


Bueno, yo controlo los 'posibles' errores desde el nivel más alto, con try /catch.

Las clases si os fijais hacen Throw( oErr ), por si fuera un error de Harbour, despues discrimino si el error es de ADO o de Harbour, no vaya a ser que sume un numerico + "ALAAA" y le estemos echando la culpa a ADO
cuando realmente no tiene la culpa. ;-)

Por ejemplo;

Code: Select all  Expand view
try
  oConnection:BeginTrans()
  Cmd := TAdoStoreProc():New( "SP_CREAR_EJEMPO", oConnection )
  oAdoCmd := oCmd:GetADOCommand() // Obtemos el objeto ADO Command.
  oADOCmd:Parameters:Item('@pHASTA'):Value := DTOC(  oDbMPrecios:Hasta )

oCmd:Execute()
catch oError
  oConnection:RollBackTrans()
  ? "ERROR. Se ha realizado ROLLBACK." + CRLF  )
  lResult := .F.
  if !ShowError( oError, oConnection )
     ? "Falla el procedimiento / Sentencia SQL"
  endif
end
#define NTRIM(n)    ( LTrim( Str( n ) ) )

Function ShowError( oError, oConnection )
   Local cErrorLog := "", n, aStack := {}
   Local lError := .F.

   if Empty( oConnection:oError:aErrors ) // No son errores de ADO
     lError := .T.
     cErrorLog += "Operation: "+ oError:Operation + CRLF
     cErrorLog += "Error description: "+ oError:Description + CRLF
     if ValType( oError:Args ) == "A"
        cErrorLog += "   Args:" + CRLF
         for n := 1 to Len( oError:Args )
             cErrorLog += "     [" + Str( n, 4 ) + "] = " + ValType( oError:Args[ n ] ) + ;
                          "   " + cValToChar( oError:Args[ n ] ) + CRLF
         next
     endif
     cErrorLog += CRLF + "Stack Calls" + CRLF
     n := 1
     while ( n < 5 ) // Las ultimas 5
          if ! Empty(ProcName( n ) )
             AAdd( aStack, "   Called from: " + ProcFile( n ) + " => " + Trim( ProcName( n ) ) + ;
                           "(" + NTRIM( ProcLine( n ) ) + ")" )
             cErrorLog += ATail( aStack ) + CRLF
          endif
          n++
     end
     MsgStop( cErrorLog , "Error Harbour" )
   endif

   oConnection:oError:ShowErrors() // Enseña algo, si tienes que enseñar

return lError



También existe otra forma, CreateParameter(), pero no he profundizado en ello, os lo dejo como ejercicio.

Bueno, dar las gracias a J.Capel por compartir su clases de ADO, esto es un complemento a ese trabajo.


Saludos
Rafa Carmona
Saludos
Rafa Carmona ( rafa.thefullARROBAgmail.com___quitalineas__)
User avatar
thefull
 
Posts: 730
Joined: Fri Oct 07, 2005 7:42 am
Location: Barcelona

Return to FiveWin para Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 32 guests