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

时间:2019-05-12 12:54:44

标签: delphi firedac tdataset 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;

我编写了以下代码,但将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 个答案:

答案 0 :(得分:5)

有些事情,如果不是“错误的”,则与您的代码不太相符。

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

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

  3. 您应该在循环之前调用FDMemTable1.DisableControls,在循环之后调用FDMemTable1.EnableControls。这样可以防止像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]);

Btw#2:使用FindKey的时间是在设置要查找的键之后,通过调用SetKey而不是设置键值来进行。

对于数据集的常规导航,请使用标准导航方法,例如NextPriorFirstLastMoveBy等。

答案 1 :(得分:3)

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;