Listando las funciones externas

Deja un comentario

Como recordarás, Firebird nos permite usar funciones externas en nuestras bases de datos. Una función externa no está incluida en la instalación del Firebird sino que la agregamos después y a cada Base de Datos que la necesite utilizar. Eso significa que cada una de nuestras bases de datos puede tener cero, una, o varias funciones externas.

Si queremos saber cuales son las funciones externas que hemos registrado en una Base de Datos, podemos usar nuestro administrador gráfico para ello:

externa01

Captura 1. Si haces clic en la imagen la verás más grande

o podemos escribir un SELECT que nos de esa información:

Listado 1.

SELECT
   *
FROM
   RDB$FUNCTIONS
WHERE
   RDB$SYSTEM_FLAG = 0

Donde obtendremos un resultado similar al siguiente:

externa02

Captura 2. Si haces clic en la imagen la verás más grande

  • RDB$FUNCTION_NAME. Es el nombre con el cual se conoce a esta función dentro de nuestra Base de Datos
  • RDB$FUNCTION_TYPE. Tipo de la función, no está siendo usado y por eso siempre es Null
  • RDB$QUERY_NAME. Nombre de la consulta, no está siendo usada y por eso siempre es Null
  • RDB$DESCRIPTION. Comentarios que podemos escribir para describir lo que hace la función
  • RDB$MODULE_NAME. El nombre que tiene el archivo .DLL o el objeto compartido, en el disco duro u otro dispositivo de almacenamiento
  • RDB$ENTRYPOINT. El nombre que tiene la función externa en el archivo .DLL o en el objeto compartido. No siempre es igual a RDB$FUNCTION_NAME
  • RDB$RETURN_ARGUMENT. El número de posición del argumento que se devuelve, en la lista de argumentos de entrada
  • RDB$SYSTEM_FLAG. Una bandera que indica si la función fue definida internamente o externamente. 0=definida externamente, 1=definida internamente

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

 

 

 

Un ejemplo del uso de funciones en Firebird

Deja un comentario

En este artículo ya habíamos visto que actualmente (versión 2.5.2) el Firebird no dispone de funciones nativamente pero que fácilmente podemos simular que sí las tiene.

https://firebird21.wordpress.com/2014/04/20/usando-un-stored-procedure-como-una-funcion/

Ahora, veremos un ejemplo práctico de como usar una función.

Ejemplo. Obteniendo la parte gravada y el impuesto

El Gobierno cobra un impuesto por cada venta que se realiza en el país. El valor monetario de ese impuesto se calcula como un porcentaje sobre lo que se le cobra al cliente. Por lo tanto:

PrecioVenta = ImporteGravado + Impuesto     (1)

Como el Impuesto es siempre un porcentaje sobre el ImporteGravado entonces la fórmula es:

Impuesto = ImporteGravado * PorcentajeImpuesto / 100     (2)

Y reemplazando la fórmula del Impuesto en (1) obtenemos:

PrecioVenta = ImporteGravado + ImporteGravado * PorcentajeImpuesto / 100     (3)

Que también podemos escribir como:

PrecioVenta = ImporteGravado * (1 + PorcentajeImpuesto / 100)     (4)

En una tabla llamada FACTURAS tenemos guardado el PrecioVenta y también el PorcentajeImpuesto y lo que queremos hallar es:

  1. El ImporteGravado
  2. El Impuesto

 Para ello, escribimos el siguiente stored procedure seleccionable:

CREATE PROCEDURE DISCRIMINAR_VENTA(
   tnPrecioVenta NUMERIC(12, 2),
   tnPorcentaje  NUMERIC(5, 2))
RETURNS(
   ftnGravado  NUMERIC(12, 2),
   ftnImpuesto NUMERIC(12, 2))
AS
BEGIN

   -- tnPrecioVenta = ftnGravado * (1 + tnPorcentaje / 100)

   ftnGravado = tnPrecioVenta / (1 + tnPorcentaje / 100);

   ftnImpuesto = ftnGravado * tnPorcentaje / 100;

   SUSPEND;

END;

Al cual podemos llamar de esta forma:

SELECT
   F.FAC_MONTOX,
   D.ftnGravado,
   D.ftnImpuesto,
   D.ftnGravado + D.ftnImpuesto
FROM
   FACTURAS F
LEFT JOIN
   DISCRIMINAR_VENTA(F.FAC_MONTOX, F.FAC_PORIVA) D
      ON 1 = 1

Desde luego que F.FAC_MONTOX y D.ftnGravado + D.ftnImpuesto deben ser idénticos, se escribió a ambos solamente para comprobar lo evidente.

En este caso tenemos una tabla llamada FACTURAS, dentro de la cual tenemos una columna llamada FAC_MONTOX donde guardamos el precio de venta y una columna llamada FAC_PORIVA donde guardamos el porcentaje del impuesto.

Al llamar a la función DISCRIMINAR_VENTA obtenemos la parte gravada y el impuesto que corresponden a cada venta realizada.

Artículos relacionados:

Usando un stored procedure como una función

El índice del blog Firebird21

 

Enmascarando los stored procedures

2 comentarios

Se llama “enmascarar” al hecho de llamar de una manera distinta a una función, rutina, clase o stored procedure. Las ventajas de enmascarar son que la llamada a la función, rutina, clase o stored procedure subyacente resulta más fácil para el programador, le permite escribir menos código fuente y además le asegura que siempre todo esté correcto.

Ejemplo:

Tenemos una tabla llamada MOVIMCAB (cabecera de movimientos) cuya estructura (resumida, por supuesto) es la siguiente:

ENMASCARAR2

Captura 1. Si haces clic en la imagen la verás más grande

y que tiene (entre otros) a estos datos:

ENMASCARAR1

Captura 2. Si haces clic en la imagen la verás más grande

Para saber si un determinado número de documento existe podríamos escribir un stored procedure similar al siguiente:

CREATE PROCEDURE EXISTE_NUMERO_DOCUMENTO(
   tnCodSuc TYPE OF COLUMN MOVIMCAB.MVC_CODSUC,
   tnTipDoc TYPE OF COLUMN MOVIMCAB.MVC_TIPDOC,
   tcNroDoc TYPE OF COLUMN MOVIMCAB.MVC_NRODOC)
RETURNS(
   ftcExisteDocumento D_BOOLEAN)
AS
  DECLARE VARIABLE lnIdenti TYPE OF COLUMN MOVIMCAB.MVC_IDENTI;
BEGIN

   lnIdenti = (SELECT
                  FIRST 1
                  MVC_IDENTI
               FROM
                  MOVIMCAB
               WHERE
                  MVC_CODSUC = :tnCodSuc AND
                  MVC_TIPDOC = :tnTipDoc AND
                  MVC_NRODOC = :tcNroDoc);

   ftcExisteDocumento = iif(lnIdenti > 0, 'T', 'F');

END;

Donde el dominio D_BOOLEAN está definido como:

CREATE DOMAIN D_BOOLEAN AS
   CHAR(1)
   CHECK (VALUE = 'F' OR VALUE = 'T');

Entonces, si desde nuestro programa escrito en Visual FoxPro queremos saber si un número de documento ya existe podríamos escribir algo así:

Local lcAlias, lcComando, llComandoOK, llExisteDocumento
Private pnCodSuc, pnTipDoc, pcNroDoc

   lcAlias = Alias()

   pnCodSuc = 0
   pnTipDoc = 1
   pcNroDoc = '001-001-0000001'

   lcComando = "EXECUTE PROCEDURE EXISTE_NUMERO_DOCUMENTO(?pnCodSuc, ?pnTipDoc, ?pcNroDoc)"

   llComandoOK = SQLEXEC(gnHandle, lcComando)

   llExisteDocumento = llComandoOK .and. ftcExisteDocumento = "T"

   if !Empty(lcAlias)
     select (lcAlias)
  endif

Ahora bien, supongamos que debemos averiguar si un número de documento existe no una vez sino varias veces. Escribir siempre los comandos mostrados arriba sería engorroso y una pérdida de tiempo y además propenso a errores. Mucho mejor sería enmascarar esos comandos con una función, tal como la siguiente:

FUNCTION ExisteDocumento
Parameters tnCodSuc, tnTipDoc, tcNroDoc
Local lcAlias, lcComando, llComandoOK, llExisteDocumento

   lcAlias = Alias()

   lcComando = "EXECUTE PROCEDURE EXISTE_NUMERO_DOCUMENTO(?tnCodSuc, ?tnTipDoc, ?tcNroDoc)"

   llComandoOK = SQLEXEC(gnHandle, lcComando)

   llExisteDocumento = llComandoOK .and. ftcExisteDocumento = "T"

   if !Empty(lcAlias)
      select (lcAlias)
   endif

Return(llExisteDocumento)

Y a la cual podríamos llamar así:

Local lnCodSuc, lnTipDoc, lcNroDoc, llDocumentoExiste

   lnCodSuc = 0
   lnTipDoc = 1
   lcNroDoc = '001-001-0000001'

   llDocumentoExiste = ExisteDocumento(lnCodSuc, lnTipDoc, lcNroDoc)

De esta manera, la función ExisteDocumento() de Visual FoxPro está “enmascarando” al stored procedure EXISTE_NUMERO_DOCUMENTO del Firebird.

Conclusión:

“Enmascarar” a los stored procedures nos permite escribir menos código fuente y además código fuente más seguro porque lo único que debemos verificar bien es que la función enmascaradora sea correcta. Si esa función está bien ya no deberemos preocuparnos por las llamadas al stored procedure correspondiente porque la función se encargará de todas las tareas.

Artículo relacionado:

El índice del blog Firebird21