当我尝试使用MCVE支持this question时,我被触发提出这个问题。我最近开始注意到TClientDataSet很快就会用完内存。在生产中,我遇到了一个问题,它无法加载大约60,000的数据集,这对我来说似乎非常低。客户端数据集通过连接到一个带有ADODataSet的提供程序进行连接,这个连接是正常的。我单独运行该查询并将结果输出到CSV,得到的文件大小小于30MB。所以我做了一个小测试,在客户端数据集中可以加载多达165K条记录,其中包含一个大小为4000的字符串字段。该字段的实际值只有3个字符,但对结果似乎没有影响。看起来每条记录至少占用那些4000个字符。4000 x 2字节x 165K记录= 1.3GB,因此它开始接近32位内存限制。如果我将其转换为备忘录字段,则可以轻松添加500万行。
所以问题本身有点宽泛,但我想要一个解决方案,并能够通过更有效地使用字符串空间来加载更大的数据集。字段很大的原因是它们可以包含注释。对于大多数记录,这些注释将为空或很短,因此这是一种极大的浪费空间。
program ClientDataSetTest;
{$APPTYPE CONSOLE}
uses SysUtils, DB, DBClient;
var
c: TClientDataSet;
i: Integer;
begin
c := TClientDataSet.Create(nil);
c.FieldDefs.Add('Id', ftInteger);
c.FieldDefs.Add('Test', ftString, 4000); // Actually claims this much space...
//c.FieldDefs.Add('Test', ftMemo); // Way more space efficient (and not notably slower)
//c.FieldDefs.Add('Test', ftMemo, 1); // But specifying size doesn't have any effect.
c.CreateDataSet;
try
i := 0;
while i < 5000000 do
begin
c.Append;
c['Id'] := i;
c['Test'] := 'xyz';
c.Post;
if (i mod 1000) = 0 then
WriteLn(i, c['Test']);
Inc(i);
end;
except
on e: Exception do
begin
c.Cancel;
WriteLn('Error adding row', i);
Writeln(e.ClassName, ': ', e.Message);
end;
end;
c.SaveToFile('c:\temp\output.xml', dfXML);
Writeln('Press ''any'' key');
ReadLn;
end.
所以问题本身有点宽泛,但我想要一个解决方案,并能够通过更有效地使用字符串空间来加载更大的数据集。字段很大的原因是它们可以包含注释。对于大多数记录,这些注释将为空或很短,因此这是一种极大的浪费空间。
- TClientDataSet是否可以配置为以不同的方式处理此类情况?我浏览了其属性,但找不到任何相关的内容。
- 可以通过使用不同的字段类型来解决吗?我想到了ftMemo,但它还有一些其他缺点,比如大小不能用于截断,显示问题,如TDBGrid将其显示为(MEMO),而不是实际值。
- 是否有TClientDataSet的替代品可以解决这个问题?这不仅涉及内存部分,还涉及与通过TProvider在此项目中主要使用的ADO组件的通信,因此并非所有内存数据集都能起作用。
(MEMO)
可以通过使用字段的OnGetText
事件轻松修复。我经常使用它来在网格中显示前几个字符,并在双击行以显示完整内容时打开一个带有备忘录控件的表单。截断可以在OnBeforePost
中处理。 - Ken WhiteTO_CLOB
以适应备忘录字段,就使得(Oracle 11g)查询几乎慢了10倍,需要花费数分钟才能打开!也许不是死路,但至少是受到致命伤害了。:p - GolezTrol