如何内部处理过滤后的tDataSet记录以不显示在tDBGrid结果上

SHI*_*Guk 2 delphi tdataset firedac fdmemtable

在下面的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;
Run Code Online (Sandbox Code Playgroud)

我编写了以下代码,但将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;
Run Code Online (Sandbox Code Playgroud)

我知道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;
Run Code Online (Sandbox Code Playgroud)

当我断开tFDMemTable和tDBGrid的连接或在过滤以保持最后一个网格状态之前将其设置为非活动状态时,网格将变为空白。最后的代码是最好的解决方案,还是有什么更好的方法在过滤工作时显示未过滤的结果?

Mar*_*ynA 5

如果没有错误,有几件事与您的代码不太匹配。

  1. 您应该使用Next,而不要FindNext移到数据集中的下一行。 Next移至数据集中的下一行,而FindNext移至与您已设置的搜索条件相匹配的下一行,例如,使用DataSet.SetKey; ...-阅读在线帮助以了解FindKey用法。

  2. 您不应该尝试使用For循环遍历数据集。使用While not FDMemData.Eof do循环。 Eof代表“文件末尾”,一旦数据集位于最后一行,则返回true。

  3. 您应该FDMemTable1.DisableControls在循环之前和FDMemTable1.EnableControls之后调用。这样可以防止在DBDB之类的数据库感知控件在循环内部进行更新,否则,随着网格更新,循环速度将降低。

  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;
Run Code Online (Sandbox Code Playgroud)

如果这样做,则完全不需要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;
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您可以使用下面的InsertRecord方法节省一些键入内容并获得更简洁的代码

FDMemTable1.InsertRecord(['A1', 1]);
FDMemTable1.InsertRecord(['B1', 2]);
FDMemTable1.InsertRecord(['A2', 3]);
FDMemTable1.InsertRecord(['B2', 4]);
Run Code Online (Sandbox Code Playgroud)

顺便说一句2:使用时间FindKey是在设置要查找的键之后,通过调用SetKey,然后设置键值。

对于一个数据集的普通导航,使用标准的导航方法,例如NextPriorFirstLastMoveBy等。