TClientDataSet在处理10万行以上的数据时非常慢

6
我有问题在Delphi TClientDataSet中检索数据。
用ADO的代码:
ADOQuery1.SQL.Text:='SELECT * FROM Table1 WITH (NoLock)';
DataSource1.DataSet:=ADOQuery1;
DataSource1.DataSet.Open;
DataSource1.DataSet.Last;

上面的代码使用纯ADO时,在3-6秒内返回超过180k行。
同样的代码使用TClientDataSet:
ADOQuery1.SQL.Text:='SELECT * FROM Table1 WITH (NoLock)';
CDS1.SetProvider(ADOQuery1);
DataSource1.DataSet:=CDS1;
DataSource1.DataSet.Open;
DataSource1.DataSet.Last;

以下代码返回相同数量的行(超过180k),但仅需3-4分钟。
CDS有什么问题吗? 它比使用ADO慢大约100倍。是否可能修复它?

5
我不知道答案,但是无论如何将180k(!)条记录返回给客户端都是非常糟糕的设计... - kobik
4
哪个版本的Delphi?从XE2到XE3有一个巨大的减速(以至于我们撤销了Delphi的更新),我不知道它是否已经修复。 - Jan Doggen
3
话虽如此:将所有数据提取到ClientDataSet中需要将每一行数据转换成内部CDS格式。这种格式提供“增强功能”,如双向导航、更改缓存、本地索引。所有这些都需要额外的处理和内存分配以支持内部结构。 (如果您需要这些功能,则需要进行额外的处理。如果您不需要它们,那么TClientDataSet可能不是您要求的最佳工具。) - Disillusioned
2
只是试图排除一些显而易见的问题,你没有将任何数据感知组件附加到数据源中吧?你尝试过禁用/启用控件吗? - Lieven Keersmaekers
1
禁用控件是必须的。我同意这里的大多数评论。如果要将180k行提取到客户端,则需要有严重的理由。如果您确定需要提取这么多元组,请选择不同的存储方式。TDataSet 的派生类通常很慢。为了提取数据,请优化查询以使用前向、只读游标并访问底层的Recordset接口。@mjn,它的文档描述不足,例如使用TList合适的项目数量也没有文档说明。 - TLama
显示剩余8条评论
4个回答

1
在使用纯ADO时,上面的代码能在3-6秒内返回超过180k行数据。
由于某些原因,我不希望你发布的代码返回所有180k条记录...我期望在调用TADOQuery.Open后看到加载第一批“X”条记录,然后在调用TADOQuery.Last时发送最后的“X”条记录。使用“while not EoF do”而不是“.Last”可能会更好地测试性能,因为(我假设)您实际上想浏览所有记录。
当链接到DataProvider时调用TClientDataset.Last时,它很可能执行与查询相关的while not EoF do等效操作,这将传输所有180k行记录。此外,随着您在其中拥有的记录越来越多,TClientDataset插入/追加操作往往变得越来越慢。我的最佳猜测是,每隔一段时间就必须重新分配其内存缓冲区。如果是这种情况,我还没有找到告诉TClientDataset的方法:“嘿!准备好了,即将到来的是180k条记录!”(类似于TList.SetCapacity)。
如果您使用的是旧版本的Delphi,有一件事可能会有所帮助,那就是Midas Speed Fix

实际上,标准的TDataPacketWriter.WriteDataSet不仅会循环遍历源数据集直到达到eof或请求的记录数量,而且还会在内部字段循环中写入每个其他字段值(进入结果流),并将其包装在空的try/except块中。 - Vladimir Ulchenko

0

这篇文章可能有些陈旧,但现在有很多新的Delphi程序员。以下是一些内幕。

在Delphi中使用CDS实际上是创建了一个内存表。你的查询可能会导致交换空间不足。

为了充分利用CDS,请使用DBX组件来获取数据。它们被称为“快进”游标,不会在数据库中创建带有游标的临时表。仅向前不会像ADO那样做花哨的事情。如果您需要大量数据集并进行全面更新通知和完全控制,则使用ADO。如果您需要快速浏览大量数据,并且对服务器的负载很小,那么CDS / DBX就是最好的选择。

DBX的方式更加困难。它就像一辆短跑赛车。只有First和Next适用于它们。没有更新,没有承诺,只有快速的单向关系。连接DBX /提供程序/CDS组合,您就可以拥有所有功能:速度和编辑能力。使用版本号检测另一个用户在您编辑数据时是否对数据进行了更改。研究提供程序选项以了解如何获得强大的灵活性。这几乎是Delphi中最难的部分。


抱歉,问题是为什么CDS在处理那么多记录时与Ado数据集相比如此缓慢。正确的答案是,对于超过几万条记录,它一直都很慢。最初是因为它分配内存的方式,但即使这些年来已经得到改进,它也是为小型客户端数据集而设计的,在这种情况下,180k行并不算“小”。顺便说一句,通过TDatasetProvider提供给CDS的TAdoQuery比检索相同数据的DBX TSqlQuery要快得多。 - MartynA
正确使用时,TAdoQuery 可以完成大部分 CDS 能做的花哨事情(例如其“公文包”模型),而不需要(与 DBX 不同)要求 CDS 执行客户端操作,而且它执行起来也会更快。 - MartynA

0

CDS1.LogChanges属性可以设置为false或true,但是问题是一样的。


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

-1

在加载数据之前,尝试将CDS1.LogChanges属性设置为False。由于它不是公开属性,因此需要在代码中完成。

根据帮助文件:对于大型数据集,LogChanges的True值可能会严重影响应用程序的性能。

您可以在初始加载后再打开它。


1
关闭切换日志不应影响加载时间,因为数据是通过提供程序流式传输而不是通过插入/附加传输的。 - Vladimir Ulchenko

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