Delphi ClientDataSet - 删除操作 - 为什么这么慢?

3
我在一款应用中使用TClientDataSet管理从多个CSV文件导入的大量数据,总共可能有100万条或更多。我想能够删除与特定CSV文件关联的所有数据集条目,但是删除大量条目的时间非常缓慢。
作为一个测试来尝试确定是否做了愚蠢的事情,我创建了一个简单的控制台应用程序。它所做的一切就是:
- 创建带有1个字段定义(ID)的TClientDataSet实例 - 添加100,000个记录 (需要0.1秒) - 删除50000个记录(需要约4秒,如果`LogChanges=TRUE`则需要约4.4秒)
如果我有150万个数据集项,并且想要删除50万个条目,则通过此方法删除条目需要的时间如此之长,以至于我甚至无法测量它。为解决这个问题,我现在正在使用一个变通方法:创建一个新数据集,然后将要保留的所有条目复制到新副本中并删除原始副本。除非我只从原始数据集中删除了少部分条目,否则这种方法都要快得多。
也许我没有使用最合适的方法来删除数据集项?我猜测每次删除项目时会触发一堆内部处理。是否有一种一次性删除一系列项目的方法我没注意到?也许我可以设置一个索引和一个基于该索引的范围,然后删除当前范围内的所有项目?
也许问题在于ClientDataSet而不是我?也许我需要使用其他组件。有什么建议吗?

1
也许您忘记关闭LogChanges - Ondrej Kelle
有趣的是,保持增量并不便宜。 - Ondrej Kelle
正如您提到的聚合和索引,它们可能是罪魁祸首;在批量删除操作之前尝试禁用它们,并在必要时重新启用它们(如果需要)。 (问题可能是它们在每次单个删除后都会更新,这是完全不必要的。) - Ondrej Kelle
1
我认为这是可以预料的。删除需要在文件中移动数据。 - Sertac Akyuz
1
哪个版本的Delphi?你尝试过Andreas Hausladen的MidasSpeedFix吗? - Ondrej Kelle
显示剩余14条评论
1个回答

1
我想提供一些关于我自己(临时/可能永久的)解决方法的详细信息,以便有同样问题的人感兴趣。

问题:从大型TClientDataSet(100k或更多记录)中删除大部分记录使用Delete操作比添加项目的初始时间要长得多(40倍以上)。

解决方案:将不想删除的所有记录复制到一个新数据集中,然后删除原始数据集。 [缺点:失去更改日志,需要额外的RAM?]

var
 CDS: TClientDataSet;

// initialize new CDS instance
function CreateNewCDSInstance: TCLientDataSet;
begin
 Result := TClientDataSet.Create(nil);
 Result.FieldDefs.Add('ID', ftInteger);
 Result.CreateDataSet;
 Result.LogChanges := False;
end;

// close + free CDS instance
procedure CloseCDS;
begin
 CDS.EmptyDataSet;
 CDS.Close;
 CDS.Free;
end;

// delete current record?
function CanDeleteCurrentRecord: boolean;
begin
 Result := CDS['ID'] < 50001; //in this simple example
 // in my application it would be more like:
 // "CDS['FILE_ID'] = AFileIDToDelete"
end;

// delete block of records:
procedure DeleteRecords;
var
 aNewCopy: TClientDataSet;
begin
 aNewCopy := CreateNewCDSInstance;
 CDS.First;
 while not CDS.EoF do
 begin
  if not CanDeleteCurrentRecord then
  begin
   // NB: AppendRecord takes array of values corresponding to field defintions
   aNewCopy.AppendRecord([CDS['ID']]);
  end;
  CDS.Next;
 end;
 CloseCDS;
 CDS := aNewCopy;
 //NB: If you have any aggregates/indexes defined, they must be redefined
end;

使用上面给出的示例,删除50k个项目的方法只需要94毫秒,而不是大约4秒。
然而,在提出这个问题并阅读评论时,我意识到这个解决方案更像是一种应急措施而不是治愈方法。更大的问题是,我正在处理的系统并不真正设计好处理它所需的数据量。也许问题不是“TClientDataSet的问题”,而是“我们如何使用TClientDataSet的问题”!即使修复了删除速度的问题,随着导入文件的大小和数量越来越大以及管理该数据的后续问题,性能仍然存在问题。
也许(在一个雨天!)我会开始考虑类似于SilverWarior建议的使用单独的数据集来保存每个导入的文件的方法,而不是将所有数据都放入一个巨大的内存表中。然后,删除文件就变成了删除数据集 - 还有其他潜在的好处。

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