2

在下面的 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 不允许 NEXT SEARCH 我尝试了这样的原始方式。它工作正常,但似乎有点愚蠢。

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 或在过滤之前设置为非活动状态以保持最后一个网格状态时,网格将变为空白。最后一个代码是最好的解决方案,还是有更好的方法在过滤工作时显示未过滤的结果?

4

2 回答 2

5

有几件事,如果不是“错误的”,与您的代码不完全正确。

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

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

  3. 您应该FDMemTable1.DisableControls在循环之前和FDMemTable1.EnableControls之后调用。这可以防止像 DBGrid 这样的 db-aware 控件在循环内更新,否则会在网格更新时减慢循环速度。

  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而不是设置键值来使用。

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

于 2019-05-12T13:55:25.450 回答
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;
于 2019-05-12T17:21:11.250 回答