Firebird dispone de una característica buenísima, que puede ayudarnos mucho cuando no disponemos del tiempo suficiente para trabajar directamente con la Base de Datos: EXECUTE BLOCK

¿Para qué sirve EXECUTE BLOCK?

Para ejecutar un bloque de código tal como el que puede escribirse en un stored procedure pero dentro del código fuente de nuestro lenguaje de programación. Dentro del EXECUTE BLOCK podemos tener parámetros de entrada, parámetros de salida, y variables locales.

En otras palabras, en lugar de escribir el stored procedure dentro de la Base de Datos, lo escribimos dentro de nuestro lenguaje de programación (Visual FoxPro, Visual Basic, C, C++, Delphi, Java, etc.)

¿Cuál es la sintaxis de EXECUTE BLOCK?

EXECUTE BLOCK [(<parámetros de entrada>)]

[RETURNS (<parámetros de salida>)]

AS

[<declaraciones de las variables locales>]

BEGIN

[<instrucciones que pueden escribirse en un stored procedure>]

END

A continuación se muestran varios ejemplos, todos ellos fueron realizados y comprobados con el lenguaje Visual FoxPro, pero adaptarlos a otros lenguajes debería ser fácil y sencillo.

La tabla de BANCOS tiene tres columnas:

  • BAN_CODSUC (código de la sucursal)
  • BAN_IDENTI (identificador del Banco)
  • BAN_NOMBRE (nombre del Banco)

Ejemplo 1:

TEXT TO lcBloque NOSHOW
   EXECUTE BLOCK
   AS
   BEGIN
      INSERT INTO BANCOS VALUES(0, 17, 'Prueba 1');
      INSERT INTO BANCOS VALUES(0, 18, 'Prueba 2');
      INSERT INTO BANCOS VALUES(0, 19, 'Prueba 3');
   END
ENDTEXT

lnResultado = SQLExec(gnHandle, lcBloque)

if lnResultado > 0     && Si se ejecutó exitosamente
   =SQLExec(gnHandle, "COMMIT")
else                   && Si ocurrió algún error
   =SQLExec(gnHandle, "ROLLBACK")
endif

En este ejemplo se intentó insertar 3 filas dentro de la tabla de BANCOS. Si las inserciones fueron exitosas entonces se realizó el COMMIT, en caso contrario el ROLLBACK.

Ejemplo 2:

TEXT TO lcBloque NOSHOW
   EXECUTE BLOCK
      RETURNS (cNomBan VARCHAR(60))
   AS
   BEGIN
      INSERT INTO BANCOS VALUES(0, 20, 'Prueba 4');
      INSERT INTO BANCOS VALUES(0, 21, 'Prueba 5');
      SELECT
         BAN_NOMBRE
      FROM
         BANCOS
      WHERE
         BAN_IDENTI = 20
      INTO
         :cNomBan;
      SUSPEND;
    END
ENDTEXT

lnResultado = SQLExec(gnHandle, lcBloque)

if lnResultado > 0     && Todo ocurrió exitosamente
   BROWSE
   =SQLExec(gnHandle, "COMMIT")
else                   && Ocurrió algún error
   =SQLExec(gnHandle, "ROLLBACK")
   =MessageBox("Un error ocurrió")
endif

En este ejemplo se intentó insertar 2 filas a la tabla de BANCOS y se devolvió el nombre del Banco cuyo identificador es 20 al programa de Visual FoxPro. Si no hubo error entonces lnResultado será mayor que cero y en ese caso se muestra el nombre del Banco cuyo identificador es 20.

Cuando se quiere devolver un valor (o más de un valor) del EXECUTE BLOCK entonces siempre debemos usar la sentencia SUSPEND.

Ejemplo 3:

Local lnNumeroBanco, lcNombreBanco

lnNumeroBanco = 22
lcNombreBanco = 'Prueba 6'

TEXT TO lcBloque NOSHOW
   EXECUTE BLOCK (nIdeBan Integer = ?lnNumeroBanco, cNomBan VarChar(40) = ?lcNombreBanco)
   AS
   BEGIN
      INSERT INTO BANCOS VALUES(0, :nIdeBan, :cNomBan);
   END
ENDTEXT

lnResultado = SQLPrepare(gnHandle, lcBloque)

if lnResultado < 0
   =AError(laError)
   =MessageBox("Error: " + laError[2])
endif

llResultadoOK = SQLExec(gnHandle) > 0

if llResultadoOK
   if !Empty(Alias())
      BROWSE
   endif
   =SQLExec(gnHandle, "COMMIT")
else
   =AError(laError)
   =MessageBox("Error: " + laError[2])
   =SQLExec(gnHandle, "ROLLBACK")
endif

En este ejemplo se usaron 2 parámetros de entrada. Para que funcionara previamente hubo que ejecutar la función SQLPREPARE(). Además, para saber cual fue el error ocurrido se utilizó la función AERROR() la cual en su segundo elemento tiene un mensaje descriptivo.

Tanto en el Ejemplo 1. como en el Ejemplo 2. los datos insertados eran fijos, en cambio ahora se usaron datos variables, lo cual le da mucha mayor potencia.

Ejemplo 4:

lnNumeroBanco = 23
lcNombreBanco = 'Prueba 7'

TEXT TO lcBloque NOSHOW
   EXECUTE BLOCK (nIdeBan Integer = ?lnNumeroBanco, cNomBan VarChar(40) = ?lcNombreBanco)
      RETURNS(cMayusc VarChar(80))
   AS
      DECLARE cTemp VarChar(40);
   BEGIN
      INSERT INTO BANCOS VALUES(0, :nIdeBan, Upper(:cNomBan));
      SELECT
         BAN_NOMBRE
      FROM
         BANCOS
      WHERE
         BAN_IDENTI = :nIdeBan
      INTO
         :cTemp;
      cMayusc = 'Creado en un EXECUTE BLOCK. ' || Trim(cTemp);
      SUSPEND;
   END
ENDTEXT

lnResultado = SQLPrepare(gnHandle, lcBloque)

if lnResultado < 0
   =AError(laError)
   =MessageBox("Error: " + laError[2])
endif

llResultadoOK = SQLExec(gnHandle) > 0

if llResultadoOK
   if !Empty(Alias())
      BROWSE
   endif
   =SQLExec("COMMIT")
else
   =AError(laError)
   =MessageBox("Error: " + laError[2])
   =SQLExec("ROLLBACK")
endif

Este último ejemplo tiene todo junto: parámetros de entrada, parámetros de salida y variables locales. En realidad no es útil lo que hace pero sirve para demostrar como se lo utiliza. Recuerda que siempre para devolver parámetros de salida debes escribir la palabra SUSPEND o no obtendrás los valores de dichos parámetros de salida.

Conclusión:

La instrucción EXECUTE BLOCK nos permite crear un stored procedure dentro de nuestro propio lenguaje de programación y ese EXECUTE BLOCK será luego ejecutado por el Servidor del Firebird. Esta posibilidad nos puede resultar muy útil en muchas circunstancias aunque en general es preferible que los stored procedures estén dentro de la Base de Datos y no dentro del código fuente que escribimos en un lenguaje de programación, ya que estando dentro de la Base de Datos estarán disponibles para todos los programadores, cualquiera sea el lenguaje de programación que utilicen.