ADOQuery.Locate Slow, Создать индекс - PullRequest
2 голосов
/ 16 апреля 2020

У меня есть следующая строка, чтобы найти строку в запросе.

if Query.Locate('Line;Hour;Minute',VarArrayOf([Line-400,AHour,minuteof(Start)]),[]) = true then

Это медленно, теперь я помню, что можно добавлять индексы в запрос, чтобы сам поиск находился в несколько раз быстрее. К сожалению, я не могу найти пример.

Может кто-нибудь помочь мне? С уважением Роберт

Ответы [ 2 ]

2 голосов
/ 16 апреля 2020

интересно q.

Обновление см. Обновление ниже.

Я настроил некоторые тестовые данные на моем сервере SS2014 Sql для запуска некоторых тестов, используя такой код:

ID := 1;
for Line := 1 to 1000 do begin
  for AHour := 1 to 24 do begin
    for AMinute := 1 to 60 do begin
      AdoQuery1.InsertRecord([ID, Line, AHour, AMinute]);
      Inc(ID);
      end;
    end;
  end;
end;

Затем я запустил несколько таких тестов, как этот

procedure TForm1.LocateTest1(DisableControls, UseSort : Boolean);
var
  T1 : Integer;
  Line,
  AHour,
  AMinute : Integer;
begin

  AdoQuery1.Sql.Text := 'select * from linetest order by line, ahour, aminute';
  AdoQuery1.CursorLocation := clUseClient;
  AdoQuery1.Open;
  T1 := GettickCount;
  if DisableControls then
    AdoQuery1.DisableControls;

  if UseSort then
    AdoQuery1.Recordset.Sort := 'Line,AHour,AMinute';
  Line := 1000;
  AHour := 23;
      for AMinute := 60 downto 1 do begin
        if not AdoQuery1.Locate('Line;AHour;AMinute', VarArrayOf([Line, AHour, AMinute]), []) then
          Caption := Format('Locate failed %d %d %d', [Line, AHour, AMinute]);
      end;
  Memo1.Lines.Add('Test1 : ' + IntToStr(GetTickCount - T1));
  if DisableControls then
    AdoQuery1.EnableControls;
  AdoQuery1.Close;
end;

Причина включения Disable / EnableControls была из-за результатов, о которых я сообщил здесь Почему прокрутка через ADOTable становится медленнее и медленнее ? , что вызов DisableControls оказывает огромное влияние на скорость прокрутки, даже если не задействованы элементы управления с поддержкой db.

Однако кажется, что прокрутка не оказывает существенного влияния на выполнение Locate () в TAdoQuery, потому что вызов DisableControls занял всего около 1,5 секунд из записанного времени около 26 секунд. Очевидно, что TAdoQuery.Locate не очень хорошо работает с большим количеством строк.

Идея параметра UseSort заключалась в том, чтобы выяснить, имеет ли сортировка RecordSet за AdoQuery какое-то влияние на скорость, но это нет, причина в том, что Locate вызывает TCustomAdoDataSet.LocateRecord, который все равно использует Sort.

Вы упомянули добавление индексов. К сожалению, TAdoQuery поддерживает только использование серверных индексов при выполнении запроса thq SQL, но не находит записи в полученном наборе результатов. Вы можете добавлять клиентские индексы в TAdoTable, но, согласно моему тесту, похожему на приведенный выше, к моему удивлению, они практически не влияют на скорость Locate ().

Таким образом, учитывая мои результаты, было бы гораздо проще использовать параметризованный SELECT для извлечения только интересующей строки, а не пытаться найти ее в большом наборе результатов. Alterantaivel, вы можете получить набор результатов в ClientDataSer через DatasetProvider или в FireDA C FDMemTable, et c. Гм, это зависит от того, что вы делаете в точности ...

Обновление После публикации моего исходного ответа у меня есть пара дополнительных обновлений, которые, возможно, было бы полезно включить.

  • Один из них касается способа имитации Locate с использованием вызовов методов поиска и фильтрации RecordSet в AdoQuery, который значительно быстрее (около 15 секунд), чем повторное выполнение AdoQuery1.Locate. Я все еще пробую это и опубликую другое обновление через день или два.

  • Другой - кратко упомянуть, что Locates делает FireDA C FDQuery вместо AdoQuery. Это похоже на тот же набор Locates, который занимает около 25 секунд с AdoQuery менее чем за 9 секунд, используя следующий код:

Используя FDQuery.Locate

procedure TForm2.LocateTest;
var
  T1 : Integer;
  Line,
  AHour,
  AMinute : Integer;
begin

  FDQuery1.Sql.Text := 'select * from linetest order by line, ahour desc, aminute desc';
  //FDQuery1.CursorLocation := clUseClient;
  FDQuery1.CursorKind := ckForwardOnly;
  FDQuery1.Open;

  T1 := GettickCount;
  Line := 1000;
  AHour := 1;

  for AMinute := 1 to 60 do begin
    if not FDQuery1.Locate('Line;AHour;AMinute', VarArrayOf([Line, AHour, AMinute]), []) then
      Caption := Format('Locate failed %d %d %d', [Line, AHour, AMinute]);
  end;

  Memo1.Lines.Add('Test1 : ' + IntToStr(GetTickCount - T1));
  FDQuery1.Close;
end;
1 голос
/ 16 апреля 2020

В документации сказано, что Locate переместите курсор на первую строку, соответствующую указанному c критерию поиска .
Если в вашей таблице много записей, Locate имеет значение slow .
Locate обычно используется в локальных базах данных, но в клиент-серверных RDBM лучше использовать SQL с WHERE для минимизации времени поиска и минимизации трафик данных c.

...