Загрузка миллионов записей в список строк может быть очень медленной

Как можно очень быстро загрузить миллионы записей из 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 одновременно.

Это должно сэкономить вам массу времени на каждой стороне клиента записи.

Другие вопросы по тегам