如何在内部处理过滤后的 tDataSet 记录而不显示在 tDBGrid 结果上
How to internally process filtered tDataSet records not to be shown on tDBGrid the result
在下面的 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 或在过滤前设置非活动状态以保持最后一个网格状态时,网格变为空白。最后一个代码是最好的解决方案还是有更好的方法在过滤工作时显示未过滤的结果?
有几件事,如果不是 "wrong",与您的代码不太相符。
您应该使用 Next
,而不是 FindNext
来移动到数据集中的下一行。 Next
移动到数据集中的下一行,而 FindNext
移动到与您已经设置的搜索条件匹配的下一行,例如使用 DataSet.SetKey; ...
- 阅读联机帮助了解 FindKey
用法。
您不应该尝试使用 For
循环遍历数据集;使用 While not FDMemData.Eof do
循环。 Eof
代表 'End of file',一旦数据集位于最后一行,return 为真。
您应该在循环之前调用 FDMemTable1.DisableControls
并在循环之后调用 FDMemTable1.EnableControls
。这可以防止 db-aware 像您的 DBGrid 这样的控件在循环内更新,否则会在网格更新时减慢循环速度。
除非你有充分的理由不这样做,否则请始终以与设置它相同的方法清除数据集过滤器,否则如果你忘记过滤器处于活动状态,你可能会得到一些非常混乱的错误。
尽量避免在非绝对需要时使用 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 在循环之前记录您在数据集中的位置,然后 return 记录您在循环之后的位置,如
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
等
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;
在下面的 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 或在过滤前设置非活动状态以保持最后一个网格状态时,网格变为空白。最后一个代码是最好的解决方案还是有更好的方法在过滤工作时显示未过滤的结果?
有几件事,如果不是 "wrong",与您的代码不太相符。
您应该使用
Next
,而不是FindNext
来移动到数据集中的下一行。Next
移动到数据集中的下一行,而FindNext
移动到与您已经设置的搜索条件匹配的下一行,例如使用DataSet.SetKey; ...
- 阅读联机帮助了解FindKey
用法。您不应该尝试使用
For
循环遍历数据集;使用While not FDMemData.Eof do
循环。Eof
代表 'End of file',一旦数据集位于最后一行,return 为真。您应该在循环之前调用
FDMemTable1.DisableControls
并在循环之后调用FDMemTable1.EnableControls
。这可以防止 db-aware 像您的 DBGrid 这样的控件在循环内更新,否则会在网格更新时减慢循环速度。除非你有充分的理由不这样做,否则请始终以与设置它相同的方法清除数据集过滤器,否则如果你忘记过滤器处于活动状态,你可能会得到一些非常混乱的错误。
尽量避免在非绝对需要时使用
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 在循环之前记录您在数据集中的位置,然后 return 记录您在循环之后的位置,如
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
等
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;