Delphi 10.2/11中DisableControls对带Lookup字段的FDTable/FDQuery遍历无效
问题根因
FireDAC的Lookup字段执行逻辑与ADO存在本质差异,DisableControls与AutoCalcFields := False 均无法阻止记录指针移动时Lookup字段的自动查找动作:
- ADO的Lookup字段默认会预加载全量关联数据到本地缓存,记录移动时直接从缓存取值,遍历过程几乎无额外开销
- FireDAC的Lookup字段默认不开启全局缓存,每次执行
Next/First等指针移动操作时,都会单独对关联数据集执行一次定位查询;且Lookup字段的查找逻辑不属于AutoCalcFields管控的计算事件链路,哪怕关闭自动计算字段,逐行查询的动作依然会执行。数千条记录就会对应数千次独立查询,直接导致遍历耗时暴涨。
解决方案
按优先级从高到低选择以下方案即可解决性能问题:
- 遍历前预加载Lookup字段缓存
不需要修改原有表结构和字段配置,仅在遍历逻辑前临时开启Lookup缓存、一次性拉取所有关联数据,遍历结束后还原原有配置即可,性能可与ADO遍历速度持平。参考实现代码:var i: Integer; OldLookupCacheState: array of Boolean; begin // 保存原有Lookup字段缓存配置 SetLength(OldLookupCacheState, FDTable1.FieldCount); for i := 0 to FDTable1.FieldCount - 1 do begin if FDTable1.Fields[i].FieldKind = fkLookup then begin OldLookupCacheState[i] := TLookupField(FDTable1.Fields[i]).LookupCache; TLookupField(FDTable1.Fields[i]).LookupCache := True; end; end; FDTable1.FetchLookupCache; // 一次性加载所有Lookup关联数据,避免逐行查询 // 原有遍历逻辑 FDTable1.AutoCalcFields := False; FDTable1.DisableControls; FDTable1.First; while not FDTable1.Eof do begin // 业务处理代码 FDTable1.Next; end; FDTable1.EnableControls; FDTable1.AutoCalcFields := True; // 还原字段原有配置 for i := 0 to FDTable1.FieldCount - 1 do begin if FDTable1.Fields[i].FieldKind = fkLookup then TLookupField(FDTable1.Fields[i]).LookupCache := OldLookupCacheState[i]; end; end; - 遍历逻辑改用SQL关联查询替代Lookup字段
如果遍历操作不需要绑定界面控件,直接在FDQuery的SQL语句中通过JOIN把需要关联的字段查询出来,替代持久化的Lookup字段,完全规避逐行查找的开销,是性能最优的实现方案。 - 全局调整FireDAC抓取配置(不推荐)
可将FDManager的FetchOptions.DetailOptimize属性设为True,全局开启关联数据缓存优化,但该配置会影响所有主从表、Lookup字段的默认行为,可能引发非预期的缓存一致性问题,仅适合逻辑简单的小型项目使用。
内容的提问来源于stack exchange,提问作者A.K.D.
相关产品推荐
相关产品推荐

