Как внутренне обработать отфильтрованные записи tDataSet, чтобы они не отображались в tDBGrid - результат - PullRequest
2 голосов
/ 12 мая 2019

В следующем tFDMemTable я пытаюсь суммировать значение записей, чье поле ID начинается с буквы A. A1, A2 и результат должен быть 4.

type
  TForm1 = class(TForm)
    FDMemTable1: TFDMemTable;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  _FieldDef: TFieldDef;
begin
  _FieldDef := FDMemTable1.FieldDefs.AddFieldDef;

  _FieldDef.Name := 'ID';
  _FieldDef.DataType := ftString;
  _FieldDef.Size := 5;

  _FieldDef := FDMemTable1.FieldDefs.AddFieldDef;

  _FieldDef.Name :='value';
  _FieldDef.DataType := ftInteger;

  FDMemTable1.CreateDataSet;

  FDMemTable1.Append;

  FDMemTable1.FieldValues['ID'] := 'A1';
  FDMemTable1.FieldValues['value'] := 1;

  FDMemTable1.Append;

  FDMemTable1.FieldValues['ID'] := 'B1';
  FDMemTable1.FieldValues['value'] := 2;

  FDMemTable1.Append;

  FDMemTable1.FieldValues['ID'] := 'A2';
  FDMemTable1.FieldValues['value'] := 3;

  FDMemTable1.Append;

  FDMemTable1.FieldValues['ID'] := 'B2';
  FDMemTable1.FieldValues['value'] := 4;
end;

Я написал следующий код, но он изменяет tDBGrid какфильтруют.То, что я хочу, это просто внутренний процесс, который tDBGrid должен остаться без каких-либо изменений.

procedure TForm1.Button1Click(Sender: TObject);
var
  _ValueSum: Integer;
  i: Integer;
begin
  FDMemTable1.Filter := 'ID like ' + QuotedStr('A%');

  FDMemTable1.Filtered := True;

  _ValueSum := 0;

  FDMemTable1.FindFirst;

  for i := 0 to FDMemTable1.RecordCount - 1 do
  begin
    _ValueSum := _ValueSum + FDMemTable1.FieldValues['value'];

    FDMemTable1.FindNext;
  end;

  Button1.Caption := IntToStr(_ValueSum);
end;

Я знаю, tDataSet.Locate не позволяет СЛЕДУЮЩИЙ ПОИСК, что я пробовал примитивный способ, как это.Он работает нормально, но выглядит немного глупо.

procedure TForm1.Button2Click(Sender: TObject);
var
  _ValueSum: Integer;
  i: Integer;
begin
  _ValueSum := 0;

  FDMemTable1.First;

  for i := 0 to FDMemTable1.RecordCount do
  begin
    if Copy(FDMemTable1.FieldValues['ID'], 1, 1) = 'A' then
    begin
      _ValueSum := _ValueSum + FDMemTable1.FieldValues['value'];
    end;

    FDMemTable1.FindNext;
  end;

  Button2.Caption := IntToStr(_ValueSum);
end;

Когда я отключаю tFDMemTable и tDBGrid или устанавливаю неактивным перед фильтрацией, чтобы сохранить последнее состояние сетки, сетка становится пустой.Последний код - лучшее решение или есть лучший способ, который показывает нефильтрованный результат, пока работает фильтрация?

Ответы [ 2 ]

5 голосов
/ 12 мая 2019

Есть несколько вещей, которые, если не "неправильные", не совсем верны с вашим кодом.

  1. Вы должны использовать Next, а не FindNext, чтобы перейти кследующий ряд в наборе данных.Next перемещает к следующей строке в наборе данных, тогда как FindNext перемещает к следующей строке, которая соответствует критериям поиска, которые вы уже установили, например, используя DataSet.SetKey; ... - прочитайте онлайн-справку для использования FindKey.

  2. Вы НЕ должны пытаться пройти набор данных, используя цикл For;используйте петлю While not FDMemData.Eof do.Eof означает «Конец файла» и возвращает true, когда набор данных находится в последней строке.

  3. Вы должны вызывать FDMemTable1.DisableControls перед циклом и FDMemTable1.EnableControls после него,Это предотвращает обновление элементов управления с поддержкой db, таких как DBGrid, внутри цикла, что в противном случае замедляло бы цикл при обновлении сетки.

  4. Если у вас нет веских причин не делать этого,ВСЕГДА очищайте фильтр набора данных в том же методе, в котором вы его установили, в противном случае вы можете получить некоторые очень запутанные ошибки, если забудете, что фильтр активен.

  5. Старайтесь избегать использования RecordCount привам не обязательноВ зависимости от используемой вами RDMS это может привести к значительным издержкам обработки, которых можно избежать на сервере и, возможно, в сети (поскольку при некоторых типах серверов это приведет к тому, что весь набор данных будет получен клиентом).

Измените свой первый цикл на

procedure TForm1.Button1Click(Sender: TObject);
var
  _ValueSum : Integer;
begin
  _ValueSum := 0;

  FDMemTable1.Filter := 'ID like ' + QuotedStr('A%');

  try
    FDMemTable1.DisableControls;
    FDMemTable1.First;
    while not FDMemTable1.Eof do begin
      _ValueSum:= _ValueSum + FDMemTable1.FieldByName('Value').AsInteger;
      FDMemTable1.Next;
    end
  finally
    FDMemTable1.Filter := '';
    FDMemTable1.Filtered := False;
    FDMemTable1.EnableControls;
  end;
   Button1.Caption := IntToStr(_ValueSum);
end;

Если вы сделаете это, вам вообще не понадобится ваш метод Button2Click.

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

var
  _ValueSum : Integer;
  BM : TBookMark;
begin
  _ValueSum := 0;

  BM := FDMemTable.GetBookMark;

  FDMemTable1.Filter := 'ID like ' + QuotedStr('A%');

  try
    [etc]
  finally
    FDMemTable1.Filter := '';
    FDMemTable1.Filtered := False;
    FDMemTable1.GotoBookMark(BM);
    FDMemTable1.FeeBookMark(BM);
    FDMemTable1.EnableControls;
  end;

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

FDMemTable1.InsertRecord(['A1', 1]);
FDMemTable1.InsertRecord(['B1', 2]);
FDMemTable1.InsertRecord(['A2', 3]);
FDMemTable1.InsertRecord(['B2', 4]);

Кстати # 2: Время использования FindKey наступает после того, как вы настроили ключ для поиска, с помощью вызова SetKey, чем затем установка значения ключа (с)).

Для обычной навигации по набору данных используйте стандартные методы навигации, например Next, Prior, First, Last, MoveBy и т. Д.

3 голосов
/ 12 мая 2019

FireDAC имеет еще один интересный вариант - агрегаты:

procedure TForm1.Button1Click(Sender: TObject);
begin
  FDMemTable1.Aggregates.Clear;
  with FDMemTable1.Aggregates.Add do
  begin
    Name := 'SUM';
    Expression := 'sum(iif(ID like ''A%'', value, 0))';
    Active := True;
  end;
  FDMemTable1.AggregatesActive := True;
  FDMemTable1.Refresh;
  Button1.Caption := VarToStr(FDMemTable1.Aggregates[0].Value));
end;
...