Меня заставили задать этот вопрос, когда я пытался поддержать этот вопрос с помощью MCVE.
Недавно я начал замечать, что TClientDataSet быстро исчерпывает память.У меня была проблема с производством, когда он не мог загрузить набор данных примерно с 60 000, что мне показалось на удивление низким.Набор данных клиента был подключен через провайдера с ADODataSet, который нормально загружался.Я запустил этот запрос отдельно и вывел результат в CSV, который дал мне файл размером <30 МБ. </p>
. Поэтому я провел небольшой тест, в котором я могу загрузить до 165 КБ записей в наборе данных клиента, который имеетстроковое поле размером 4000. Фактическое значение поля составляет всего 3 символа, но это не имеет значения для результата.
Похоже, что каждая запись занимает как минимум эти 4000 символов,4000 x 2 байта x 165K записей = 1,3 ГБ, так что начинается ограничение на 32-битный предел памяти.Если я превращу его в заметку, я могу легко добавить 5 миллионов строк.
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, которые решают эту проблему?Речь идет не только о части в памяти, но и об обмене данными с компонентами ADO через TProvider, что является основным способом, которым я использую его в этом проекте, поэтому ни один набор данных памяти не сработает.
Что касается последнего пункта, я случайно обнаружил этот вопрос , где он скрыт в комментариях, упоминается vgLib, но все, что я нахожу по этому поводу, это неработающие ссылки, и я даже не знаю, если эторешил бы эту проблему.Очевидно, что код C ++ для MidasLib уже доступен, но, поскольку он составляет 1,5 МБ неясного кода, я подумал, что, возможно, стоит спросить здесь, прежде чем углубляться в это.;)