在 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
你可以自己测试一下。
创建一个新的VCL项目并添加一个TFDMemTable、TDataSource、TDBGrid、TCheckBox(称为cbUseFilterExpr)和TButton
到表格。
像往常一样连接 FDMemTable、TDataSource 和 TDBGrid。
添加如下所示的 OnFilterCalls 整数表单字段和事件处理程序。
编译并运行.
代码
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 的方法
- 创建一个 public 变量
- 在数据集迭代之前将值设置为 public 变量
- 在 OnCalcEvent 中,检查要退出的值以中止事件代码执行
- 数据集迭代后。重新设置数据集书签以强制 运行 OnCalcFields 事件,否则 calcFields 列将为空
目前对我有用。
另一个想法是创建分页(分页以限制查询 return 类似于网络应用程序)。
哪个 运行 先是 TDataSet OnFilterRecord 还是 OnCalcFields 事件?
设置为 Accept = false 的记录是否在 OnCalcFields 事件中仍然可见?
如果是,是否有 属性 检查记录可见性?
代码情况就像,当数据集有更多记录,如 3k 时,
OnRecordFilter 对字符串字段进行手动过滤,以实现网格上的记录可见性(Accept = true / false),
OnCalckFields有额外的列查找其他数据集,
无论有没有过滤器,对数量列求和的函数都很慢。
当我禁用 OnCalcFields 事件时,执行速度非常快。
DataSet 是 TFDQuery,加载的初始数据是自由日期范围,因此用户可以查看 3 年或更长时间的日期范围。
ui 看起来像这样
https://i.stack.imgur.com/0XyrN.png
你可以自己测试一下。
创建一个新的VCL项目并添加一个TFDMemTable、TDataSource、TDBGrid、TCheckBox(称为cbUseFilterExpr)和TButton 到表格。
像往常一样连接 FDMemTable、TDataSource 和 TDBGrid。
添加如下所示的 OnFilterCalls 整数表单字段和事件处理程序。
编译并运行.
代码
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 的方法
- 创建一个 public 变量
- 在数据集迭代之前将值设置为 public 变量
- 在 OnCalcEvent 中,检查要退出的值以中止事件代码执行
- 数据集迭代后。重新设置数据集书签以强制 运行 OnCalcFields 事件,否则 calcFields 列将为空
目前对我有用。
另一个想法是创建分页(分页以限制查询 return 类似于网络应用程序)。