Загрузка миллионов записей в список строк может быть очень медленной
Как можно очень быстро загрузить миллионы записей из tadotable в список строк?
procedure TForm1.SlowLoadingIntoStringList(StringList: TStringList);
begin
StringList.Clear;
with SourceTable do
begin
Open;
DisableControls;
try
while not EOF do
begin
StringList.Add(FieldByName('OriginalData').AsString);
Next;
end;
finally
EnableControls;
Close;
end;
end;
6 ответов
В вашем цикле вы получаете поле. Поиск поля из цикла
procedure TForm1.SlowLoadingIntoStringList(StringList: TStringList);
var
oField: TField;
begin
StringList.Clear;
with SourceTable do
begin
Open;
DisableControls;
try
oField:= FieldByName('OriginalData');
if oField<>Nil then
begin
while not EOF do
begin
StringList.Add(oField.AsString);
Next;
end;
end;
finally
EnableControls;
Close;
end;
end;
end;
К сожалению, вы не можете сделать это быстро. Это по сути медленная операция, которая требует большого количества процессорного времени и пропускной способности памяти для достижения. Вы могли бы добавить больше оборудования, но я подозреваю, что вы должны вместо этого переосмыслить свою задачу.
С "миллионами записей" вы можете подумать: 1/ Измените свой запрос с
SELECT * FROM MYTABLE;
в
SELECT OriginalData FROM MYTABLE;
Вы будете использовать меньше памяти и будете более эффективными.
2 / Посмотрите другой компонент, чем TStringList в зависимости от ваших потребностей.
3 / Смотрите все хорошие предыдущие советы, в основном:
- не используйте FieldByName
- прямая ссылка на провайдера OleDB
Это отсортировано?
// Turn off the sort for now
StringList.Sorted := False;
// Preallocate the space
StringList.Capacity := recordCount;
// Now add the data with Append()
...
// Now turn the sort back on
StringList.Sorted := True;
Расширяя ответ @Ravaut123, я бы предложил следующий код:
Убедитесь, что ваш запрос не подключен к какому-либо другому визуальному компоненту, и для него не заданы какие-либо события, которые запускаются в строках строк, поскольку это приведет к обновлению при каждом изменении активной записи, что замедляет процесс.
Вы можете отключить визуальный контроль, используя disablecontrols
, но не события и не визуальный контроль.
...
SQLatable:= 'SELECT SingleField FROM atable ORDER BY indexedfield ASC';
AQuery:= TAdoQuery.Create(Form1);
AQuery.Connection:= ....
AQuery.SQL.Text:= SQLatable;
Использование запроса гарантирует, что вы выбираете только 1 поле, в том порядке, в котором вы хотите, это уменьшает сетевой трафик. Таблица выбирает все поля, вызывая гораздо больше накладных расходов.
function TForm1.LoadingAllIntoStringList(AQuery: TAdoQuery): TStringList;
var
Field1: TField;
begin
Result:= nil;
try
if not(AQuery.Active) then begin
AQuery.Open;
end else begin
AQuery.First;
end;
AQuery.DisableControls;
AQuery.Filtered:= false; //Filter in the SQL `where` clause
AQuery.FetchAll; //Preload all data into memory
Result:= TStringlist.Create;
except
{ignore error, will return nil}
end;
try
Result.Sorted:= false; //Make sure you don't enable sorting
Result.Capacity:= AQuery.RecordCount; //Preallocate the needed space
Field1:= AQuery.FieldByName('SingleField'); //Never use `fieldbyname` in a loop!
while not AQuery.EOF do begin
Result.Add(Field1.AsString);
AQuery.Next;
end; {while}
AQuery.EnableControls;
except
FreeAndNil(Result);
end;
Если вы хотите загрузить данные в список строк, чтобы выполнить некоторую обработку, попробуйте сделать это в операторе SQL. БД может использовать индексы и другие оптимизации, которые не может использовать список строк.
Если вы хотите сохранить эти данные в файл CSV, рассмотрите возможность использования встроенной функции БД для этого.
например, MySQL имеет:
SELECT X FROM table1 INTO OUTFILE 'c:/filename_of_csv_file.txt'
Который создаст файл CSV для вас.
Многие БД имеют симметричные функции.
Шутки в сторону? Миллионы записей в списке строк?
Хорошо, давайте предположим, что вы действительно должны принять этот подход...
Уже есть несколько хороших предложений.
Если вы хотите поэкспериментировать с другим подходом, вы можете рассмотреть возможность объединения отдельных частей сервера записей (с помощью хранимой процедуры), а затем вернуть объединенные данные в виде большого двоичного объекта (или, возможно, nvarchar(max)), который в основном представляет собой список объединенных строк скажем, возврат каретки (при условии, что это разумный разделитель для ваших нужд).
Затем вы можете просто присвоить возвращаемое значение свойству Text в TStringList.
Даже если вы не можете выполнить все струны одним ударом, вы можете выполнять их группами, скажем, по 1000 одновременно.
Это должно сэкономить вам массу времени на каждой стороне клиента записи.