Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Impresión (https://www.clubdelphi.com/foros/forumdisplay.php?f=4)
-   -   Optimización en exportación a Excel (https://www.clubdelphi.com/foros/showthread.php?t=95629)

novato_erick 25-03-2022 01:30:19

Optimización en exportación a Excel
 
Saludos primeramente Chicos:

realizando consulta por el club y encontrando varias códigos de interés a mi propósito para exportación desde Delphi utilizando ClientDataSet seguí dichas indicaciones de esta forma:

Código Delphi [-]
procedure EmiteInformeVenta(FechaVentasIni, FechaVentasFin:TDate;
  mGridExport :TDataSet);
  var
   rangoExcel : Excel2000.ExcelRange;
   I, Fila : Integer;
   mMarcador : TBookmark;
   mTabla : TDataSet;
   AplicacionExcel:  TExcelApplication;
begin
with  dmReportes.qInformeVentaNombreClientes do
begin
  ParamByName('FECHAINI').AsDate := FechaVentasIni;
  ParamByName('FECHAFIN').AsDate := FechaVentasFin;
  ExecSQL;
end;
  if dmReportes.cdsInformeVentaNombreCliente.Active = True then
  begin
    dmReportes.cdsInformeVentaNombreCliente.EmptyDataSet;
    dmReportes.cdsInformeVentaNombreCliente.Active := False;
  end;
  dmReportes.cdsInformeVentaNombreCliente.Active := True;
  //procedimiento de ajdsoft   https://www.ajpdsoft.com/modules.php...owpage&pid=117
  //Creamos el libro de Excel (Abriendo Excel)
  AplicacionExcel := TExcelApplication.Create(Nil);
  //Asociamos el dataset (tabla) del DBGrid con la tabla
  //que utilizaremos en esta función
  mTabla := dmReportes.cdsInformeVentaNombreCliente;
  AplicacionExcel.Visible[0] := True;
  //Creamos el nuevo libro de Excel
  AplicacionExcel.ConnectKind := ckNewInstance;
//https://delphi.cjcsoft.net/viewthread.php?tid=46003
//mejoras de codigo porque me enviaba despues de 900 registro error raised exception class EOleException with message 'OLE error 800AC472'. delphi
  AplicacionExcel.Connect;
  AplicacionExcel.Workbooks.Add(NULL, 0);
  //primera fila con los nombres de las columnas
  //para ello recorremos todos las columnas de la tabla
  //y mostramos en Excel el valor de "DisplayLabel"
  rangoExcel := AplicacionExcel.ActiveCell;
  for I := 0 to mTabla.FieldCount -1 do
    begin
      rangoExcel.Value := mTabla.Fields[i].DisplayLabel;
      rangoExcel := rangoExcel.Next;
    end;
  //aplicamos un autoformato de Excel a las filas y columnas añadidas
  rangoExcel.AutoFormat(10, NULL, NULL, NULL, NULL, NULL, NULL);
  mTabla.DisableControls;
  try
    mMarcador := mTabla.Bookmark;
       try
     //recorremos todos los registros de la tabla para ir añadiéndolos
     //a la hoja actual de Excel
      mTabla.First;
      Fila := 2;
      while not mTabla.Eof do
      begin
        rangoExcel := AplicacionExcel.Range['A' + IntToStr(Fila), 'A' +
         InttoStr(Fila)];
        for I := 0 to mTabla.Fields.Count -1 do
          begin
            rangoExcel.Value := mTabla.Fields[i].AsString;
            rangoExcel := rangoExcel.Next;
          end;
          mTabla.Next;
          Inc(Fila);
      end;
       finally
         mTabla.Bookmark := mMarcador;
         mTabla.EnableControls;
       end;
  finally
     AplicacionExcel.Disconnect;
     AplicacionExcel.Free;
  end;
end;

en fin al ejecutar la exportación de 10,000 mil registro es demasiado lento casi 4 minutos.

Alguien podría ayudarme a la optimización para que sea más rápida la exportación ?

Agradeciendo su colaboración;

Saludos;

novato_erick

Neftali [Germán.Estévez] 25-03-2022 10:04:30

Así viendo el código por encima, no parece que se pueda hacer muchas cosa en él para optimizarlo.
Primero hace un recorrido por los títulos y luego para cada fila/registro (WHILE) recorre las columnas (FOR) para obtener los valores.

No veo mucha posible mejora, salvo que intentes utilizar Threads. Pero en ese caso no hablamos de reducir el tiempo, sino que al utilizar threads, lo que consigues es hacerlo en un proceso "secundario" y no "bloquear" la aplicación principal.

Lo otro que se me ocurre (que esto sí puede que reduzca el tiempo), es que intentes conectar a excel utilizando ADO, en lugar de TExcelApplication.
Es decir, puedes configurar una conexión a Excel utilizando el TADOconnection, de forma que la hoja de Excel se consulta como una tabla.

En los foros ya hemos hablado del tema (ADO+Excel); Busca mensajes antiguos para que puedas ver ejemplos de conexión y diría que en el FTP puedes encontrar incluso ejemplos de ello.
Si no encuentras nada, dilo.

fjcg02 25-03-2022 10:55:24

Hola,
tal como lo planteas abriendo el excel como un objeto OLE no se puede acelerar. El tiempo se va en la sincronización de la exportación de la aplicación con el excel, celda a celda.

Si sólo trabajas con una pestaña, puedes exportar a csv y luego cargar el dato en excel.

Si no, las alternativas que tienes son:
- utilizar alguna libería que genere los ficheros excel directamente ( las hay gratuitas aunque no sé cual recomendarte)
- utilizar las hojas excel como tablas tal y como te indica Neftalí.

La diferencia es sustancial.

Un saludo

marco3k 25-03-2022 15:23:15

Solucion a Excel lento
 
Mira en la parte final de este post respondi esa pregunta, espero te sirva:


https://www.clubdelphi.com/foros/showthread.php?t=94464

mRoman 25-03-2022 22:06:23

Componentes de FireSoft
 
Hola Novato_Erick

En esta página https://torry.net/authorsmore.php?id=6057 podras bajar unos componentes que te puedan ayudar (Autor:Federico Firenze). FireSoft ExportSuite V.1, son Free.

Talvez te ayuden a ser mas rápido la exportación.

Saludos.

novato_erick 26-03-2022 00:09:24

Cita:

Empezado por marco3k (Mensaje 546227)
Mira en la parte final de este post respondi esa pregunta, espero te sirva:


https://www.clubdelphi.com/foros/showthread.php?t=94464


Tenias razón marcos utilizando el Componente ADO la exportación fue en dos pestañazos :D.

Esto fué lo que implementé Gracias a ustedes:
Código Delphi [-]
procedure EmiteInformeVentas2(FechaVentasI, FechaVentasF: TDate); // Ver2
var
  I, fila: Integer;
  ExcelApp, ExcelLibro, ExcelHoja: OleVariant;
  RutaExcel, FechaInfo: String;
begin
  with dmReportes.qInformeVentaNombreClientes do
  begin
    ParamByName('FECHAINI').AsDate := FechaVentasI;
    ParamByName('FECHAFIN').AsDate := FechaVentasF;
    ExecSQL;
  end;
  if dmReportes.cdsInformeVentaNombreCliente.Active = True then
  begin
    dmReportes.cdsInformeVentaNombreCliente.EmptyDataSet;
    dmReportes.cdsInformeVentaNombreCliente.Active := False;
  end;
  dmReportes.cdsInformeVentaNombreCliente.Active := True;
  TotalRegistroData := dmReportes.cdsInformeVentaNombreCliente.RecordCount;
  dmReportes.ADODataSetInforme.FieldDefs.Assign
    (dmReportes.cdsInformeVentaNombreCliente.FieldDefs);
  dmReportes.ADODataSetInforme.CreateDataSet;
  try
    dmReportes.cdsInformeVentaNombreCliente.DisableControls;
    // dmReportes.ADODataSetInforme.Active := True;
    dmReportes.ADODataSetInforme.DisableControls;
    dmReportes.cdsInformeVentaNombreCliente.First;
    while not dmReportes.cdsInformeVentaNombreCliente.Eof do
    begin
      dmReportes.ADODataSetInforme.Insert;
      for I := 0 to dmReportes.cdsInformeVentaNombreCliente.FieldCount - 1 do
      begin
        dmReportes.ADODataSetInforme.Fields[i].Value :=
          dmReportes.cdsInformeVentaNombreCliente.Fields[i].Value;
        // dmReportes.ADODataSetInforme.Post;
      end;
      dmReportes.cdsInformeVentaNombreCliente.Next;
    end;
    FechaInfo := StringReplace(DateToStr(Now), '/', '', [rfReplaceAll]);
    RutaExcel := ExtractFilePath(Application.ExeName) + 'Informe' + FechaInfo;
    // dmReportes.ADODataSetInforme.SaveToFile(RutaExcel + '.xls');
  finally
    dmReportes.ADODataSetInforme.EnableControls;
    dmReportes.cdsInformeVentaNombreCliente.EnableControls;
  end;
    // Crea el objeto Excel, el objeto workBook y el objeto sheet
    ExcelApp := CreateOleObject('Excel.Application');
  try
    ExcelLibro := ExcelApp.Workbooks.Add();
    // Añadimos una Hoja
    ExcelHoja := ExcelLibro.Worksheets.Add();
    // aca puedes recorrer los encabezados y otras cosas que desees No he logrado poner los encabezado :confused:
    ExcelHoja.Range['A2'].CopyFromRecordset
      (dmReportes.ADODataSetInforme.Recordset);  // .CopyFromRecordset(Rs.Recordset);
    // esta es el método importante del Excel y como parametro tiene un objeto ADO
    // y por eso la necesidad de usar este objeto.
    MessageDLG('¡Se exportó correctamente!', mtInformation, [mbOk], 0);
    ExcelApp.Visible := True;
  except
    ExcelApp.Quit;
    dmReportes.ADODataSetInforme.EnableControls;
    Showmessage('No se pudo crear el Archivo Excel.');
    raise;
  end;
end;

como coloque en el comentario: "No he logrado poner los encabezado" de la tabla :confused::confused::confused:

igual está funcional mejoraré y al términar pondré el código final.

Saludos;

novato_erick

novato_erick 26-03-2022 00:14:11

Se me olvido poner que en mi ADODataSetInforme le puse en la propiedad ConnectionString: Provider=Microsoft.Jet.OLEDB.4.0; Mode=ReadWrite;Extended Properties=Excel 8.0;Persist Security Info=False

Saludos;

novato_erick

marco3k 28-03-2022 22:17:03

Cita:

Empezado por novato_erick (Mensaje 546255)
Tenias razón marcos utilizando el Componente ADO la exportación fue en dos pestañazos :D.

Esto fué lo que implementé Gracias a ustedes:
Código Delphi [-]procedure EmiteInformeVentas2(FechaVentasI, FechaVentasF: TDate); // Ver2 var I, fila: Integer; ExcelApp, ExcelLibro, ExcelHoja: OleVariant; RutaExcel, FechaInfo: String; begin with dmReportes.qInformeVentaNombreClientes do begin ParamByName('FECHAINI').AsDate := FechaVentasI; ParamByName('FECHAFIN').AsDate := FechaVentasF; ExecSQL; end; if dmReportes.cdsInformeVentaNombreCliente.Active = True then begin dmReportes.cdsInformeVentaNombreCliente.EmptyDataSet; dmReportes.cdsInformeVentaNombreCliente.Active := False; end; dmReportes.cdsInformeVentaNombreCliente.Active := True; TotalRegistroData := dmReportes.cdsInformeVentaNombreCliente.RecordCount; dmReportes.ADODataSetInforme.FieldDefs.Assign (dmReportes.cdsInformeVentaNombreCliente.FieldDefs); dmReportes.ADODataSetInforme.CreateDataSet; try dmReportes.cdsInformeVentaNombreCliente.DisableControls; // dmReportes.ADODataSetInforme.Active := True; dmReportes.ADODataSetInforme.DisableControls; dmReportes.cdsInformeVentaNombreCliente.First; while not dmReportes.cdsInformeVentaNombreCliente.Eof do begin dmReportes.ADODataSetInforme.Insert; for I := 0 to dmReportes.cdsInformeVentaNombreCliente.FieldCount - 1 do begin dmReportes.ADODataSetInforme.Fields[i].Value := dmReportes.cdsInformeVentaNombreCliente.Fields[i].Value; // dmReportes.ADODataSetInforme.Post; end; dmReportes.cdsInformeVentaNombreCliente.Next; end; FechaInfo := StringReplace(DateToStr(Now), '/', '', [rfReplaceAll]); RutaExcel := ExtractFilePath(Application.ExeName) + 'Informe' + FechaInfo; // dmReportes.ADODataSetInforme.SaveToFile(RutaExcel + '.xls'); finally dmReportes.ADODataSetInforme.EnableControls; dmReportes.cdsInformeVentaNombreCliente.EnableControls; end; // Crea el objeto Excel, el objeto workBook y el objeto sheet ExcelApp := CreateOleObject('Excel.Application'); try ExcelLibro := ExcelApp.Workbooks.Add(); // Añadimos una Hoja ExcelHoja := ExcelLibro.Worksheets.Add(); // aca puedes recorrer los encabezados y otras cosas que desees No he logrado poner los encabezado :confused: ExcelHoja.Range['A2'].CopyFromRecordset (dmReportes.ADODataSetInforme.Recordset); // .CopyFromRecordset(Rs.Recordset); // esta es el método importante del Excel y como parametro tiene un objeto ADO // y por eso la necesidad de usar este objeto. MessageDLG('¡Se exportó correctamente!', mtInformation, [mbOk], 0); ExcelApp.Visible := True; except ExcelApp.Quit; dmReportes.ADODataSetInforme.EnableControls; Showmessage('No se pudo crear el Archivo Excel.'); raise; end; end;


como coloque en el comentario: "No he logrado poner los encabezado" de la tabla :confused::confused::confused:

igual está funcional mejoraré y al términar pondré el código final.

Saludos;

novato_erick


Agregar encabezados en sencillo, por ejemplo agrega esto mas o menos así:

Código Delphi [-]
var o_Excel,o_Libro, o_Hoja : OleVariant; 
begin
    o_Excel := CreateOleObject('Excel.Application');    
    o_Libro := o_Excel.Workbooks.Add;    
    o_hoja := o_Libro.Worksheets.Add;   
    o_hoja.range['A1']:='Código';    
    o_hoja.range['B1']:='Nombre';    
    o_hoja.range['C1']:='Fecha';    
    o_hoja.range['D1']:='Valor';    
    o_hoja.range['E1']:='Estado';
    ...
end;

novato_erick 29-03-2022 21:54:52

Cita:

Empezado por marco3k (Mensaje 546275)
Agregar encabezados en sencillo, por ejemplo agrega esto mas o menos así:

Código Delphi [-]
var o_Excel,o_Libro, o_Hoja : OleVariant; 
begin
    o_Excel := CreateOleObject('Excel.Application');    
    o_Libro := o_Excel.Workbooks.Add;    
    o_hoja := o_Libro.Worksheets.Add;   
    o_hoja.range['A1']:='Código';    
    o_hoja.range['B1']:='Nombre';    
    o_hoja.range['C1']:='Fecha';    
    o_hoja.range['D1']:='Valor';    
    o_hoja.range['E1']:='Estado';
    ...
end;


Ohhh gracias... Quedo de anillo al dedo.^\||/^\||/^\||/

Saludos y Bendiciones por su ayuda.

novato_erick


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

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