在 TDataSet OnFilterRecord 事件中过滤掉记录的可见性

Visibility of filtered out records in TDataSet OnFilterRecord event

哪个 运行 先是 TDataSet OnFilterRecord 还是 OnCalcFields 事件?
设置为 Accept = false 的记录是否在 OnCalcFields 事件中仍然可见?
如果是,是否有 属性 检查记录可见性?

代码情况就像,当数据集有更多记录,如 3k 时,
OnRecordFilter 对字符串字段进行手动过滤,以实现网格上的记录可见性(Accept = true / false),
OnCalckFields有额外的列查找其他数据集,
无论有没有过滤器,对数量列求和的函数都很慢。
当我禁用 OnCalcFields 事件时,执行速度非常快。

DataSet 是 TFDQuery,加载的初始数据是自由日期范围,因此用户可以查看 3 年或更长时间的日期范围。
ui 看起来像这样 https://i.stack.imgur.com/0XyrN.png

你可以自己测试一下。

  1. 创建一个新的VCL项目并添加一个TFDMemTable、TDataSource、TDBGrid、TCheckBox(称为cbUseFilterExpr)和TButton 到表格。

  2. 像往常一样连接 FDMemTable、TDataSource 和 TDBGrid。

  3. 添加如下所示的 OnFilterCalls 整数表单字段和事件处理程序。

  4. 编译并运行.

代码

procedure TForm1.FormCreate(Sender: TObject);
var
  AField : TField;
  i : Integer;
begin
  AField := TIntegerField.Create(Self);
  AField.FieldName := 'ID';
  AField.DataSet := FDMemTable1;

  AField := TStringField.Create(Self);
  AField.FieldName := 'Name';
  AField.DataSet := FDMemTable1;

  FDMemTable1.CreateDataSet;

  for i := 1 to 100 do
    FDMemTable1.InsertRecord([1, 'Name' + IntToStr(i)]);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  OnFilterCalls := 0;

  FDMemTable1.Filtered := False;
  if cbUseFilterExpr.Checked then
     FDMemTable1.Filter := 'Name= ''Name1'''
   else
     FDMemTable1.Filter := '';
   FDMemTable1.Filtered := True;
   ShowMessage('OnFilterCalls ' + IntToStr(OnFilterCalls));
end;

procedure TForm1.FDMemTable1FilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
begin
  Inc(OnFilterCalls);
end;

应用程序使用 100 条记录填充 FDMemTable1。 OnFilterCalls 变量将 计算激活过滤时调用的次数OnFilterRecord

点击 Button1 会在 FDMemTable1 上设置一个过滤器,这取决于是否 cbUseFilterExpr 是否被选中:如果是,过滤使用一个过滤器表达式,它只 匹配记录 ID=1。 ShowMessage显示的结果是1,呵呵,OnFilterRecord 事件只被调用一次。如果 cbUseFilterExpr 选中 ShowMessage 显示值 100。

结论:对于 FDMemTable(以及我自信地预测的其他 FireDAC 数据集类型),OnFilterRecord 为匹配 FDMemTable 的 Filter 表达式的每个记录调用一次事件,如果有的话, 如果 Filter 表达式为空,则为数据集中的每条记录一次。哇,OnFilterRecord 仅针对匹配 Filter 表达式的记录调用,如果有的话,那么它的行为就好像 OnFilterRecord 被称为通过 Filter 表达式“过滤后”,因此在 FireDAC 的情况下,您的问题的答案是“否”,表达式过滤的记录在 OnFilterRecord 事件中不可见。

如评论中所述,TDataSet 没有定义数据集如何处理过滤,而是 特定于实现,并且在不同的数据集组件库之间可能有所不同。

更新 您仍然没有提供任何关于您在代码中到底在做什么的详细信息 (并且考虑到您的 q 可能因缺少调试细节而关闭),但是 我想你可以确信我上面所说的也适用于你的情况。简单地放置一个调试器断点 在 TForm1.FDMemTable1FilterRecord(DataSet: TDataSet 中的 end 上。 运行 应用程序并选中 cbUseFilterExpr 复选框。当 bp 触发时,重复按调试器单步执行 F8 直到你在方法 TFDDatSView.Rebuild 中登陆单位 FireDAC.DatS。您会看到您处于 for 循环中,

      for i := iBegin to iEnd do begin
        ...

这是在对数据集应用过滤时执行的循环,对数据集中的每条记录执行一次,并来自 for 循环的内容 只需调用 OnFilterRecord 事件就可以让自己满意 对于任何可见的记录,因为它满足任何有效的过滤器表达式。

这是我对这个 OnCalcEvent 问题所做的解决方法,但我的查询没有确切答案。

解决方案:
一种在加载大量初始记录的情况下遍历数据集时跳过 OnCalcEvent 的方法

  1. 创建一个 public 变量
  2. 在数据集迭代之前将值设置为 public 变量
  3. 在 OnCalcEvent 中,检查要退出的值以中止事件代码执行
  4. 数据集迭代后。重新设置数据集书签以强制 运行 OnCalcFields 事件,否则 calcFields 列将为空

目前对我有用。

另一个想法是创建分页(分页以限制查询 return 类似于网络应用程序)。