Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Firebird e Interbase (https://www.clubdelphi.com/foros/forumdisplay.php?f=19)
-   -   UDF Firebird (https://www.clubdelphi.com/foros/showthread.php?t=66726)

subzero 09-03-2010 16:26:45

UDF Firebird
 
Hola foreros.

Tras la necesidad de generar ciertos calculos dentro de una consulta y por la cantidad de datos para dicha operación, me he puesto a realizar una dll simple desde delphi 2005 la cual incluye unicamente la función que se debe ejecutar a la cual recibe los parametros por valor, pués bien hasta aqui todo bien, vinculo la dll y hago el llamamiento de la función desde firebird (ver 2.1) y en m equipo funciona perfecto!....

El problema radica cuando la instalo en otro equipo con winxp... instalo firebird 2.1 y en la carpeta de UDF agrego la dll que he creado que funciona en mi maquina, genero la consulta y me muestra el siguiente error:

Cita:

Invalid Token
Invalid request BLR at offset 707.
Function MI_FUNCION is not defined.
Module name or entrypoint could not be found.
Alguien puede ayudarme a solucionar este problema.

jhonny 09-03-2010 16:43:36

Aún no declaras la función en la base de datos ;)

subzero 09-03-2010 17:02:31

Creo que si la tengo declara es más ejecuto el siguiente script

Código SQL [-]
DECLARE EXTERNAL FUNCTION ECUACION
  DECIMAL(18, 4),
  DECIMAL(18, 4),
  DECIMAL(18, 4),
  DECIMAL(18, 4),
  DECIMAL(18, 4),
  DECIMAL(18, 4),
  DECIMAL(18, 4)
RETURNS DECIMAL(18, 4) BY VALUE
ENTRY_POINT 'ecuacion' MODULE_NAME 'MIS_UDFS';

Para más info anexo el codigo de la dll hecha en delphi:

Código Delphi [-]
library MIS_UDFS;

uses
  SysUtils,
  Classes;

function ecuacion(var cant, bonif, costo, desc, desc2, desc3, present : real) : Real;stdcall;
var dt1, dt2, dt3, costo_dts : real;
begin
  dt1 := 0; dt2  := 0; dt3  := 0; costo_dts  := 0;
  dt1       := Costo * (desc/100);
  costo_dts := Costo - dt1;
  dt2       := costo_dts * (desc2/100);
  costo_dts := costo_dts - dt2;
  dt3       := costo_dts * (desc3/100);
  costo_dts := costo_dts - dt3;
  costo_dts := (costo_dts * cant)/(cant + bonif);
  result := costo_dts / Present;
end;

exports ecuacion;

begin
end.

mightydragonlor 09-03-2010 18:36:02

las udf deben declararse en cada maquina que las vaya a usar, no estoy seguro, pero creo que la udf va vinculada al motor y no a la base de datos.

guillotmarc 09-03-2010 19:05:35

El mensaje de error es, como dice Jhonny, de que no está declarada. Pero está claro que si ejecutas ese script entonces debería estarlo. Así que no se me ocurre donde está el error. Si en un Servidor Firebird te funciona, en otro también debería hacerlo.

Yo te sugiero de que no te compliques la vida con UDF's (que siempre es un problema a la hora de distribuir la aplicación) y que utilices un procedimiento almacenado.

donde ahora tienes algo como :

select idventa, ..., ecuacion(cantidad, bonif, costo, ...)
from XXX

ahora puedes hacer :

select idventa, ..., (select RESULTADO from ECUACION(cantidad, bonif, costo, ...))
from XXX

Ya solo tienes que programar el procedimiento almacenado ECUACION para que haga el mismo cálculo que tu Dll.

NOTA: no te olvides poner un SUSPEND; al final del procedimiento almacenado. Ya que el resultado tiene que devolverse como un Dataset para que sea leído en la subconsulta donde se va a utilizar.

Saludos.

jhonny 09-03-2010 19:16:35

Cita:

Empezado por guillotmarc (Mensaje 356230)
El mensaje de error es, como dice Jhonny, de que no está declarada. Pero está claro que si ejecutas ese script entonces debería estarlo. Así que no se me ocurre donde está el error. Si en un Servidor Firebird te funciona, en otro también debería hacerlo.

En ese caso, a mi se me acurre que si dicho Script esta siendo ejecutado con IBExpert, FlameRobin o similares... quizá lo estas ejecutando pero se te haya olvidado hacer el commit... bueno, es lo que se me ocurre de momento :S.

subzero 09-03-2010 23:54:59

Efectivamente realize pruebas con la ecuacion en un procedimiento almacenado y definitivamente menos dolores de cabeza... le agradezco a todos por su aporte, especialmente a guillotmarc, pués aprendí una utilidad diferente para con los procedimientos almacenados.

Casimiro Notevi 10-03-2010 00:04:11

¿Desde qué versión es posible hacer eso?, no recuerdo haber hecho uso de esa particularidad.

guillotmarc 10-03-2010 19:40:53

Hola Casimiro.

Que yo sepa ha sido posible hacerlo desde siempre, desde Firebird 1 (que es cuando empecé a utilizar Interbase/Firebird). Que yo recuerde, en los procedimientos almacenados siempre se ha podido añadir el suspend (que normalmente utilizas con un for select) para que devuelva el resultado como un dataset.

Aunque esto normalmente se usa para leer un dataset en tu programa (en lugar de utilizando un select), también puedes utilizar el procedimiento almacenado dentro de un select, en una union, en una subconsulta, etc. ...

Así que un buen día me di cuenta de que eso podia ser muy útil, ya que además de utilizarlo para consultar datos también podría servir para definirme mi propia librería de funciones en PSQL, y no tener que depender tanto de udf's externas.

La verdad es que desde entonces lo vengo usando de forma masiva.

Me es especialmente imprescindible cuando tengo que importar datos. Muchas veces tengo que programar importaciones hacia nuestro sistema de las gestiones que puedan tener nuevos clientes. Con lo que te encuentras que tienes que trabajar con todo tipo de datos, y que muchas veces los clientes lo tiene en un simple campo de texto, mal formateado, y que te las tienes que apañar para convertirlo, por ejemplo, en un timestamp.

Por eso tengo una librería de funciones como esta (en este caso intenta convertir una cadena en fecha) :

Código SQL [-]
CREATE PROCEDURE "X_IMPORT_Data" (
    VALOR VARCHAR(50))
RETURNS (
    DATA TIMESTAMP)
AS
DECLARE VARIABLE DIA INTEGER;
DECLARE VARIABLE MES INTEGER;
DECLARE VARIABLE ANO INTEGER;
DECLARE VARIABLE I INTEGER;
DECLARE VARIABLE TMP VARCHAR(50);
begin
  DATA = null;
  I = 1;
  while (substring(:VALOR from I for 1) not in ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')) do begin
    I = I + 1;
  end
  TMP = '';
  while (substring(:VALOR from I for 1) in ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')) do begin
    TMP = TMP || substring(VALOR from I for 1);
    I = I + 1;
  end
  DIA = cast(TMP as integer);
  while (substring(:VALOR from I for 1) not in ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')) do begin
    I = I + 1;
  end
  TMP = '';
  while (substring(:VALOR from I for 1) in ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')) do begin
    TMP = TMP || substring(VALOR from I for 1);
    I = I + 1;
  end
  MES = cast(TMP as integer);
  while (substring(:VALOR from I for 1) not in ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')) do begin
    I = I + 1;
  end
  TMP = '';
  while (substring(:VALOR from I for 1) in ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0')) do begin
    TMP = TMP || substring(VALOR from I for 1);
    I = I + 1;
  end
  ANO = cast(TMP as integer);
  if (:ANO < 20) then ANO = 2000 + ANO;              /* Tracto l'any, ja que pot estar en 2 digits */
  else if (:ANO < 100) then ANO = 1900 + ANO;
  TMP = cast(MES as varchar(20)) || '-' || cast(DIA as varchar(20)) || '-' || cast(ANO as varchar(20));
  DATA = cast(TMP as TIMESTAMP);
  suspend;
  when any do exit;    /* En cas d'Error retorno Null */
end
^

Creo una tablas temporales de importacion, con la estructura de los datos a importar, y hago un DataPump, con lo que ya tengo todos los datos en una única base de datos Firebird.

Luego ya solo tengo que hacer los correspondentes INSERT INTO *** SELECT **** FROM IMPORT_**** para poner los datos de las tablas temporales en las tables correspondientes de nuestro sistema, y puedo utilizar esas funciones como si fueran UDF's, con lo que he recortado drasticamente el tiempo que tardo en hacer una importacion de datos (antes me tenía que hacer un programa en Delphi ex-profeso en cada ocasión).

Saludos

Casimiro Notevi 10-03-2010 20:41:08

Gracias por la aclaración, guillotmarc , aunque realmente estaba preguntando por lo de añadir un procedimiento almacenado en un select, por ejemplo: "select * from PA_ArticulosRecibidosCompras(100)"
Pero releyendo lo escrito me he dado cuenta que he dicho una tontería, últimamente ando bastante "disperso" y cometiendo errores sinsentido.
No sé, será la edad :D o que necesito unas vacaciones de varios años (pagadas, por supuesto) :)


La franja horaria es GMT +2. Ahora son las 23:08:07.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi