从SQLite导出到Delphi中的Excel

3

我正在使用以下过程从SqlLite中使用TSQLConnection导出数据:

  Campos := TStringList.Create;
  SQLiteConnection.Execute('SELECT * FROM tabela', nil, results);
  if not results.IsEmpty then
  begin
    results.GetFieldNames(Campos);

    XLApp1 := createoleobject('excel.application');
    XLApp1.Workbooks.Add(xlWBatWorkSheet);
    Sheet := XLApp1.Workbooks[1].WorkSheets[1];
    linha := 1;
    begin
       pBar.Max := results.RecordCount;
       for coluna := 0 to results.FieldCount - 1 do
           Sheet.Cells[1, coluna + 1]  :=  Campos[coluna];

       while ( not results.Eof ) do
       begin
          linha := linha + 1;
          for coluna := 0 to results.FieldCount - 1 do
              Sheet.Cells[linha, coluna + 1]  :=  results.FieldByName(campos[coluna]).AsString;
          results.next;
          pBar.Position := pBar.Position + 1;
          Application.ProcessMessages;
       end;
   end;

);

它能正常工作。但是完成任务需要太长时间了。在拥有SSD的i7笔记本上,导出一个包含40,000个记录和45个字段的表格需要35分钟。

因此我的问题是:是否有可能更快一些?


首先,删除 Application.ProcessMessages。大多数人认为它可以神奇地加速程序运行,但实际上会拖慢程序的运行速度。因此,永远不应该使用它。 - Jerry Dodge
你为什么要连续两次分配“Sheet”?难道你没有收到编译器提示“分配给'Sheet'的值从未被使用”吗? - Jerry Dodge
@JerryDodge 即使我使用进度条,也不能为用户提供太多反馈。在一个耗时较长的例程中,这并不是一个好主意。 - Reginaldo Rigo
1
假设您将清理代码(从强制消息泵以更新UI并将此代码移动到线程中以通过索引访问字段),则可以尝试使用ODBC驱动程序来连接MS Excel。也许它比OLE自动化更快。 - Victoria
2
如果您正在使用application.processmessage来移动进度条,请参阅此文章http://zarko-gajic.iz.hr/delphi-tprogressbar-not-updating-fast-enough/。这可能无法解决您的问题,因为40000条记录需要一些时间...最好将其导出到.csv,然后导入Excel。 - John Easley
显示剩余8条评论
1个回答

5
最简单的方法是将所有数据收集到一个变量数组中,然后一次性传递给Excel。大部分时间都花费在向Excel写入数据上,将其减少为一次写操作会更快。
下面的代码改编自从TStringGrid传输数据到Excel工作表的代码。"Results"来自您的原始代码,因此请考虑它已经按照上面指示的方式设置好了。 (由于我没有您的数据进行测试,因此下面的代码未经过测试。它可以与stringgrid一起使用,正如我之前提到的那样-我只是无法测试适应性。如果遇到问题,请留下评论,我会尽力解决。)
对于35个字段的40K行数据,您可能需要将其分成块(即使每次处理几百行也比逐行处理要快得多)。
var
  xls, wb, Range: OLEVariant;
  arrData: Variant;
  RowCount, ColCount, i, j: Integer;
begin
  // Set up your dataset as Result here, just as in your own code
  // ....

  //create variant array where we'll copy our data
  RowCount := Results.RecordCount;
  ColCount := StringGrid1.FieldCount;
  arrData := VarArrayCreate([1, RowCount, 1, ColCount], varVariant);

  //fill array
  j := 1;
  while not Results.Eof do
  begin
    for i := 1 to ColCount do
      arrData[i, j] := Results.Fields[i].AsString;
    Inc(j);
    Results.Next;
  end;

  //initialize an instance of Excel
  xls := CreateOLEObject('Excel.Application');

  //create workbook
  wb := xls.Workbooks.Add;

  //retrieve a range where data must be placed
  Range := wb.WorkSheets[1].Range[wb.WorkSheets[1].Cells[1, 1],
                                  wb.WorkSheets[1].Cells[RowCount, ColCount]];

  //copy data from allocated variant array
  Range.Value := arrData;

  //show Excel with our data
  xls.Visible := True;
end;

谢谢。实际上,当我提出这个问题时,我有类似的想法,因为我使用arrData从Excel中读取数据非常快,但不知道如何反过来使用它。 - Reginaldo Rigo
我已经测试过这个例程,并进行了细微的调整,效果很好。我把 arrData[i, j] := Results.Fields[i].AsString; 改成了 arrData[j, i] := Results.Fields[i-1].AsString; 现在可以正常使用了。它很好,而且非常快,但是当涉及到 Range.Value := arrData 时,复制所有数据需要大约25分钟。 - Reginaldo Rigo
@ReginaldoRigo:那就不要试图一次性完成所有任务。可以分成小批量逐步完成。 - Ken White
这并没有帮助到任何事情。我已经尝试将其分成每1000个项目一组,但每组需要大约28秒的时间。因此,现在对我来说,将其保存为CSV文件是最好的选择。 - Reginaldo Rigo
@ReginaldoRigo:那么你肯定做错了什么。我曾经使用上述代码将大量数据从Delphi导出到Excel。也许你不应该尝试导出40K行35个字段的数据。你是每次都打开和关闭Excel吗? - Ken White

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接