Ir al contenido


Foto

Recuperar un Blob desde BBDD en un StoreProcedure

StoreProcedure sql delphi blob

Mejor respuesta santiago14 , 17 enero 2017 - 01:42

Bueno, después de mucho probar y fracasar pude hacer funcionar. Jajaja, me salió verso...

Bueno, ahí va:


delphi
  1. function TDataModule1.recupero_Valor_Entorno(cod_sucursal:integer; nombre_Entorno:string):TStream;
  2. begin
  3. with DataModule1.spValorEntorno do
  4. begin
  5. Close;
  6. Params.Clear;
  7. StoredProcName:='RECUPERO_VALOR_ENTORNO '; //nombre del store procedure
  8. Params.CreateParam(ftInteger, 'COD_SUCURSAL', ptInput); //creamos el parametro de entrada
  9. Params.CreateParam(ftWideString, 'NOMBRE_ENTORNO', ptInput); //creamos el parametro de entrada
  10. //Params.CreateParam(ftWideString, 'VALOR', ptOutput); //creamos el parametro de salida
  11. Params.CreateParam(ftBlob, 'VALOR', ptOutput); //creamos el parametro de salida
  12. ParamByName('COD_SUCURSAL').AsInteger:=cod_sucursal;
  13. ParamByName('NOMBRE_ENTORNO').AsString:=nombre_entorno ; //le asignamos al param. entrada su valor
  14.  
  15. Prepare; //hacemos que se "arme" el objeto SP
  16. try
  17. OpenOrExecute;
  18. if FieldByName('VALOR') is TBlobField then
  19. begin
  20. Result:=CreateBlobStream(spValorEntorno.FieldByName('VALOR'), bmRead);
  21. end;
  22. unPrepare;
  23. except
  24. on exception do
  25. begin
  26. UnPrepare;
  27. raise;
  28. end;
  29. end; //del try
  30. end;
  31. end;

Mil gracias por todos los aportes, sirvieron de mucho. Este Procedure tiene la capacidad de devolver un Stream, que luego lo transformo convenientemente en lo que yo quiera.

Gracias de nuevo.

 

Ir al mensaje completo


  • Por favor identifícate para responder
6 respuestas en este tema

#1 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 16 enero 2017 - 04:58

Buenas, antes que nada, feliz 2.017 para todos. Que sea un mejor año.
Si este Post no está en el lugar adecuado, pido por favor que lo envíen a donde corresponda. Mientras no sea el basurero. ;)
 
Explico: Tengo un proceso en Delphi XE5 que recupera algunas cosas desde la BBDD (Firebird 2.5), este proceso llama a un Store Procedure de Firebird.
El tema está cuando quiero recuperar datos que son Blob's. Siempre me devuelve vacío.
 
Mi proceso es:

delphi
  1. function TDataModule1.recupero_Valor_Entorno(cod_sucursal:integer; nombre_Entorno:string):TStream;
  2. //var
  3. // resultado:TStream;
  4. begin
  5. //spValorEntorno es un componente StoreProc de FireDAC
  6. with DataModule1.spValorEntorno do
  7. begin
  8. Close;
  9. Params.Clear;
  10. StoredProcName:='RECUPERO_VALOR_ENTORNO '; //nombre del store procedure
  11. Params.CreateParam(ftInteger, 'COD_SUCURSAL', ptInput); //creamos el parametro de entrada
  12. Params.CreateParam(ftWideString, 'NOMBRE_ENTORNO', ptInput); //creamos el parametro de entrada
  13. //Quiero que me devuelva un valor Blob
  14. Params.CreateParam(ftBlob, 'VALOR', ptOutput); //creamos el parametro de salida
  15. ParamByName('COD_SUCURSAL').AsInteger:=cod_sucursal;
  16. ParamByName('NOMBRE_ENTORNO').AsString:=nombre_entorno ; //le asignamos al param. entrada su valor
  17.  
  18. Prepare; //hacemos que se "arme" el objeto SP
  19. try
  20. ExecProc;
  21. //que me devuelva lo que esta en el parametro de salida
  22.  
  23. //Aquí, por supuesto me tira un error.
  24. Result:=ParamByName('VALOR').AsBlob; //capturamos lo que devolvió el SP
  25.  
  26. unPrepare;
  27. except
  28. on exception do
  29. begin
  30. UnPrepare;
  31. raise;
  32. end;
  33. end; //del try
  34. end;
  35. end;

Mi Procedimiento almacenado es:

sql
  1. CREATE OR ALTER PROCEDURE RECUPERO_VALOR_ENTORNO (
  2. COD_SUCURSAL INTEGER,
  3. NOMBRE_ENTORNO VARCHAR(100))
  4. RETURNS (
  5. VALOR BLOB sub_type 0 segment SIZE 100)
  6. AS
  7. DECLARE variable CODIGO_ENTORNO INTEGER;
  8. BEGIN
  9. /*Ya no actualiza en Entorno, ahora lo hace en Valores*/
  10. SELECT e.cod_entorno
  11. FROM entorno e
  12. WHERE e.nombre_entorno = :nombre_entorno
  13. INTO :codigo_entorno;
  14.  
  15. /* Recupero el valor entorno según el nombre_entorno solicitado */
  16. SELECT FIRST 1 v.valor FROM valores v
  17. WHERE v.cod_valor IN (SELECT se.cod_valor
  18. FROM sucursales_entorno se
  19. WHERE se.cod_sucursal = :cod_sucursal
  20. AND se.cod_entorno = :codigo_entorno)
  21. INTO :valor;
  22. SUSPEND;
  23. END

Haciendo pruebas dentro de la BBDD recupera bien las cosas, pero cuando lo llamo, devuelve datos vacíos, o mi Procedimiento no sabe recuperar bien los datos, o algo...
 
El campo "v.valor" es un Blob definido en la tabla "Valores"
 
Bueno, espero haber sido claro...
Gracias.

  • 0

#2 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 16 enero 2017 - 05:32

Debes guardarlo en un stream, algo así de rápido:


delphi
  1. if Dataset.FieldByName('campo') is TBlobField then begin
  2. TBlobField(DataSet.FieldByName('campo')).SaveToStream(variable);
  3. Variable.Seek(0, soFromBeginning);
  4. //Continúas tratando el Stream
  5. ...
  6. end;

Al no saber qué tipo de datos almacenas en ese campo no puedo dar más detalles.


  • 0

#3 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 16 enero 2017 - 05:32

¿Que componentes estás usando? Yo tengo Delphi Berlin 10.1 Starter, y la cual no viene con suites de acceso a bases de datos. Hay que instalarlos por aparte como las Zeos por ejemplo. Si usas estos componentes puedo hacer una prueba.

 

No estoy completamente seguro, pero creo que el problema puede ser una de dos cosas (o quizá ambas):

 

1. Tu función recupero_Valor_Entorno() regresa como resultado un TStream, mientras que tu en ParamByName() pretendes asignarle un Blob, que no necesariamente es lo mismo. Puede que te devuelva vacío ya que no es tan directo el pase de BLOB a TStream.

 

2. Cuando creas el parámetro para el campo de retorno VALOR le estableces como tipo ptOutPut. Es posible que en este caso debiera ser ptResult.

 

Como alternativa al punto 2, en lugar de recuperarlo por ParamByName() quizá sea necesario mediante FieldByName(). Esto te lo comento debido a que existe la posibilidad de que este tipo de SP al regresar un blob sea del tipo seleccionable. Aún cuando se trate de un único registro, y mono atributo. Es un dataset, de todas formas.

 

Saludos,


  • 0

#4 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 17 enero 2017 - 05:50

Debes guardarlo en un stream, algo así de rápido:


delphi
  1. if Dataset.FieldByName('campo') is TBlobField then begin
  2. TBlobField(DataSet.FieldByName('campo')).SaveToStream(variable);
  3. Variable.Seek(0, soFromBeginning);
  4. //Continúas tratando el Stream
  5. ...
  6. end;

Al no saber qué tipo de datos almacenas en ese campo no puedo dar más detalles.

 

En ese campo hay almacenado: imágenes, direcciones web, datos de facturación (CUIT, IIBB, etc.) Esto puede reconocerse a través del nombre. Así , si NOMBRE_ENTORNO = 'IMAGEN_FACTURA', ese Blob tiene el logo de la empresa que va en los comprobantes de venta.


sql
  1. CREATE TABLE ENTORNO (
  2. COD_ENTORNO INTEGER NOT NULL,
  3. NOMBRE_ENTORNO VARCHAR(50),
  4. OBSERVAC_ENTORNO VARCHAR(500),
  5. AGRUPAMIENTO VARCHAR(200)
  6. );

A través de NOMBRE_ENTORNO entro a otra tabla, VALORES:


sql
  1. CREATE TABLE VALORES (
  2. COD_VALOR INTEGER NOT NULL,
  3. VALOR BLOB SUB_TYPE 0 SEGMENT SIZE 80 CHARACTER SET ISO8859_1,
  4. OBS_VALOR VARCHAR(500),
  5. VALOR_EDITABLE BOOLEANO /* BOOLEANO = CHAR(2) NOT NULL CHECK(VALUE IN ('SI', 'si', 'NO', 'no')) */
  6. );

Y aquí está el Blob, que puede contener cualquier cosa. Esa "cualquier cosa" está identificada por NOMBRE_ENTORNO, que me dice que es lo que hay ahí.

 

Y es aquí donde entra en juego el Store Procedure, motivo del Post.

 

 

Espero haber sido claro.

Gracias.


Editado por santiago14, 17 enero 2017 - 05:52 .

  • 0

#5 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 17 enero 2017 - 07:27

Estaba claro desde el principio mi estimado, el stored procedure, o más bien, el motor firebird, devuelve los campos Blob como stream, claro, siempre y cuando sea binario, si es texto te devuelve sin problemas, la clave está en cómo tratas esa información en Delphi, tanto Delphius como yo, te recomendamos el uso de TStream, ahora bien, debes crear algún algoritmo para tratar la información de acuerdo al tipo de dato guardado en ese campo, como mi ejemplo anterior, si es texto:

delphi
  1. var Texto: String;
  2.  
  3. ...
  4.  
  5. if Dataset.FieldByName('campo') is TBlobField then begin
  6. TBlobField(DataSet.FieldByName('campo')).SaveToStream(variable);
  7. Variable.Seek(0, soFromBeginning);
  8. Texto := Variable.readString(Variable.Size);
  9. end;

En las imágenes es más complejo pero a la vez más sencillo, porque debes determinar que formato es (jpg,png bitmap, etc), en este caso te doy un ejemplo con JPG:

delphi
  1. var logo: TJPEGImage;
  2. begin
  3. try
  4. logo := TJPEGImage.create;
  5. logo.assign(Dataset.FieldByName('blob'));
  6. Image1.assign(logo);
  7. finally
  8. logo.free;
  9. end;
  10. end;

En fin, el Stored Procedure nunca te va a devolver el campo BLOB ya "tratado", debes tratarlo en el entorno que desarrolles.
  • 0

#6 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 17 enero 2017 - 08:05

Perfecto, esa fue la idea desde un principio. Necesitaba que el Store Procedure me devolviera un TStream. Luego, en una siguiente instancia, paso ese resultado al tipo que corresponda.

Para ello, tengo algunos métodos interesantes: StreamToString, StreamToPicture, ...

 

Gracias.


  • 0

#7 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 17 enero 2017 - 01:42   Mejor respuesta

Bueno, después de mucho probar y fracasar pude hacer funcionar. Jajaja, me salió verso...

Bueno, ahí va:


delphi
  1. function TDataModule1.recupero_Valor_Entorno(cod_sucursal:integer; nombre_Entorno:string):TStream;
  2. begin
  3. with DataModule1.spValorEntorno do
  4. begin
  5. Close;
  6. Params.Clear;
  7. StoredProcName:='RECUPERO_VALOR_ENTORNO '; //nombre del store procedure
  8. Params.CreateParam(ftInteger, 'COD_SUCURSAL', ptInput); //creamos el parametro de entrada
  9. Params.CreateParam(ftWideString, 'NOMBRE_ENTORNO', ptInput); //creamos el parametro de entrada
  10. //Params.CreateParam(ftWideString, 'VALOR', ptOutput); //creamos el parametro de salida
  11. Params.CreateParam(ftBlob, 'VALOR', ptOutput); //creamos el parametro de salida
  12. ParamByName('COD_SUCURSAL').AsInteger:=cod_sucursal;
  13. ParamByName('NOMBRE_ENTORNO').AsString:=nombre_entorno ; //le asignamos al param. entrada su valor
  14.  
  15. Prepare; //hacemos que se "arme" el objeto SP
  16. try
  17. OpenOrExecute;
  18. if FieldByName('VALOR') is TBlobField then
  19. begin
  20. Result:=CreateBlobStream(spValorEntorno.FieldByName('VALOR'), bmRead);
  21. end;
  22. unPrepare;
  23. except
  24. on exception do
  25. begin
  26. UnPrepare;
  27. raise;
  28. end;
  29. end; //del try
  30. end;
  31. end;

Mil gracias por todos los aportes, sirvieron de mucho. Este Procedure tiene la capacidad de devolver un Stream, que luego lo transformo convenientemente en lo que yo quiera.

Gracias de nuevo.

 


  • 1





Etiquetado también con una o más de estas palabras: StoreProcedure, sql, delphi, blob

IP.Board spam blocked by CleanTalk.