Быстрый SQL-запрос, но медленный поиск результата - PullRequest
6 голосов
/ 08 февраля 2012

Я использую Advantage Database Server от Sybase, и на данный момент у меня есть хороший быстрый запрос на соединение слева, который выполняется очень быстро.Проблема в том, что после выполнения запроса я хотел бы поместить результаты в строку.Я получил набор данных из 55000 записей.Теперь это занимает до 16 сек.положить его в строку.Мой запрос занял всего 8 мс.Моя первая попытка была такой:

    aADSQuery.Open
    aADSQuery.First
    WHILE not aADSQuery.eof do
    begin
       s := s + aADSQuery.FieldbyName('Name').asString+',';
       aADSQuery.Next;
    end;

После этого я попытался избежать aADSQuery.next, но aADSQuery.RecordCount занял у меня 9 секунд.

    aADSQuery.Open
    aADSQuery.First
    Count := aADSQuery.RecordCount;
    for i:=0 to count-1 do
    begin
      aADSQuery.RecNo := i;
      aADSQuery.FieldbyName('Name').AsString; 
    end;

База данных проиндексированас первичным ключом для идентификатора записи и индексируется для других столбцов.Я думал о создании представления для подсчета моих записей, чтобы избежать количества записей, которое может точно так же, как счетчик sql.Но подсчет записей из представления занял то же время, что и раньше.Если я использую счетчик sql в моей базовой таблице с 130000 записями, это займет всего 200 мс.Но если я делаю подсчет на моей итоговой таблице, без использования представления у меня уходит 9 с.Я уверен, что это так, потому что нет никаких индексов для новой временной таблицы результатов.Кто-нибудь знает, как правильно решить эту проблему или как получить более быстрый результат?

Большое спасибо

Ответы [ 4 ]

13 голосов
/ 08 февраля 2012

Используйте некоторый буферный класс, такой как TStringStream, для заполнения строки. это позволит избежать медленного перераспределения конкатенации String (s := s + foo).

Не используйте aADSQuery.FieldbyName('Name').AsString в цикле. Это медленно. Вместо этого создайте локальную переменную F следующим образом:

var
  F: TField;

F := aADSQuery.FieldbyName('Name');
for i:=0 to count-1 do
begin
  aADSQuery.RecNo := i;
  F.AsString; 
end;

Я считаю, что использование aADSQuery.Next быстрее, чем использование RecNo

procedure Test;
var
  F: TField;
  Buf: TStringStream;
  S: string;
begin
  aADSQuery.DisableControls;
  try
    aADSQuery.Open;
    F := aADSQuery.FieldbyName('Name');
    Buf := TStringStream.Create('');
    try
      while not aADSQuery.Eof do
      begin
        Buf.WriteString(F.AsString + ',');
        aADSQuery.Next;
      end;
      S := Buf.DataString;
    finally
      Buf.Free;
    end;
  finally
    aADSQuery.EnableControls;
  end;
end;

Вы можете сгенерировать эту строку на стороне сервера и вернуть ее на стороне клиента без необходимости создавать какие-либо строки на стороне клиента:

DECLARE @Names NVARCHAR(max)
SELECT @Names = ''
SELECT @Names = @Names + ',' + ISNULL([Name], '') FROM MyTable
SELECT @Names

Также вы можете оптимизировать производительность, установив TAdsQuery.AdsTableOptions. Убедитесь, что AdsFilterOptions установлено на IGNORE_WHEN_COUNTING и AdsFreshRecordCount установлено на False.

2 голосов
/ 08 февраля 2012

Из ОП мне не совсем понятно, если цель состоит в том, чтобы найти общее количество записей или показать данные пользователю. Если это для отображения данных, то объединение всех данных для 55 000 записей в одну строку, вероятно, не лучший подход. Если вы довольны производительностью выполнения запроса в Advantage Data Architect, то, вероятно, имеет смысл использовать аналогичный подход и сохранять данные в какой-то сетке.

Например, связать TDataSource с TDBGrid и связать запрос с источником данных:

AdsQuery1.Open;
DataSource1.DataSet:=AdsQuery1;
DBGrid1.DataSource:=DataSource1;

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

Редактировать Когда вы запрашиваете количество записей, весь набор результатов должен быть разрешен сервером. Если вы используете Advantage Local Server и если данные находятся на сетевом сервере, то при чтении всех данных в сети потребуются дополнительные расходы. Если вы используете Advantage Database Server (версия клиент / сервер), то обработка будет происходить на сервере и может быть намного быстрее.

Конечно, это зависит от запроса, но 9 секунд для разрешения набора результатов могут быть слишком длинными. В Advantage Data Architect вы можете проверить оптимизацию запроса. В меню SQL есть опция «Показать план», а также кнопка на панели инструментов в утилите SQL для отображения плана запроса. Возможно, вам не хватает необходимого индекса.

0 голосов
/ 12 февраля 2012

Итак, наконец, я нашел свои ошибки, но некоторые вещи я не понимаю. Прежде всего я изменил свой SQL-запрос левого соединения внутри запроса на соединение. Это сделало мой рекорд быстрее, а также, если я использую следующий, теперь это быстрее. Поэтому после этого я проверил типы таблиц для каждого столбца. И я обнаружил, что не очень удобно использовать фиксированный размер для символьного столбца, если в этом нет необходимости. В моем случае я выбрал размер 100 для 20 столбцов, но размер столбцов увеличивается с 1 до 20 с шагом в три. Таким образом, максимальный размер столбца 20 равен 60, а столбец 1 содержит 3 символа. (Это мои поисковые столбцы). Это привело к тому, что предложение while do уже в два раза быстрее. С этими изменениями. Я мог получить мои 55000 записей за 5500 мс. Теперь я изменил дизайн стола. Я положил все в одну таблицу, что мне больше не нужно соединение. По крайней мере, на данный момент. Я использовал обычный пункт Select .. From .. Where. Мое время для этих 55000 результатов ввода было сокращено снова до 2500 мс. Это более чем хорошо для меня. Таким образом, единственный вопрос по-прежнему состоит в том, почему это имеет большое значение при получении данных после выполнения ADSQuery.open, если я использую другой SQL-запрос. Я думал, что это может повлиять на время выполнения запроса SQL, но это также влияет на получение результатов.

0 голосов
/ 08 февраля 2012

Почему бы вам не выполнить то, что вам нужно на стороне сервера, а просто вернуть результат?

Более того, в такой ситуации:

  • Вы заставляете Delphi перераспределять строку каждый раз. Если вам известен наибольший размер, который он может достичь, вы должны предварительно выделить размер строки, а затем «исправить» ее, когда закончите.
  • FieldByName () медленный - он должен выполнять поиск для каждого вызова. Определите поля, которые вы собираетесь использовать для набора данных.
  • Производительность сети может иметь значение, если записи «большие». Вы можете установить MTU на большее значение, чтобы лучше использовать современные гигабитные сети.
...