You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TParam.ParamRef的用途是什么?及性能耗时问题解析

优化TParam.ParamRef的性能问题(针对大量参数场景)

我之前在处理含大量参数的数据库查询时,也碰到过一模一样的性能瓶颈——TParam.ParamRef因为反复调用无索引的ParamByName做线性搜索,导致整体查询效率骤降。先把你提到的原实现贴出来:

function TParam.ParamRef: TParam;
begin
  if not Assigned(FParamRef) then
    if Assigned(Collection) and (Name <> '') then
      FParamRef := TParams(Collection).ParamByName(Name)
    else
      FParamRef := nil;
  Result := FParamRef;
end;

问题根源分析

ParamByName的默认实现是遍历整个TParams集合,逐个比对参数名称来找到匹配项,时间复杂度是O(n)。当参数数量达到几十甚至上百个时,每次调用ParamRef都会触发一次全量遍历,批量执行查询时的累计耗时会非常可观。

可行的优化方案

方案1:给TParams添加哈希表索引(最优解)

如果能修改TParams类的实现,最彻底的优化是给它加一个哈希表来缓存参数名称与对象的映射,把查找复杂度降到O(1)。具体步骤如下:

  • 在TParams类声明中添加私有成员:FParamMap: TDictionary<string, TParam>;
  • 在TParams的构造函数初始化字典,析构函数中释放;
  • 重写Add、Delete、Clear等修改参数集合的方法,同步更新哈希表;
  • 修改ParamByName,优先从哈希表查找,兼容旧逻辑同时处理大小写(大多数数据库参数名称不区分大小写)。

修改后的ParamByName示例:

function TParams.ParamByName(const Value: string): TParam;
var
  NormalizedName: string;
begin
  NormalizedName := LowerCase(Value);
  // 优先从哈希表快速查找
  if FParamMap.TryGetValue(NormalizedName, Result) then
    Exit;
  // 旧线性搜索逻辑,处理未缓存的情况(比如动态新增参数)
  for Result in Self do
    if LowerCase(Result.Name) = NormalizedName then
    begin
      FParamMap.Add(NormalizedName, Result);
      Exit;
    end;
  raise EParamNotFound.CreateFmt('参数 %s 未找到', [Value]);
end;

方案2:修改TParam.ParamRef,缓存查找结果

如果无法修改TParams的结构,也可以优化ParamRef的实现,确保只做一次线性搜索,后续直接返回缓存的结果:

function TParam.ParamRef: TParam;
var
  I: Integer;
  Params: TParams;
begin
  if not Assigned(FParamRef) then
  begin
    FParamRef := nil;
    if Assigned(Collection) and (Name <> '') then
    begin
      Params := TParams(Collection);
      // 仅初始化时做一次线性搜索
      for I := 0 to Params.Count - 1 do
        if SameText(Params[I].Name, Name) then
        begin
          FParamRef := Params[I];
          Break;
        end;
    end;
  end;
  Result := FParamRef;
end;

这个方案虽然还是线性搜索,但避免了每次调用ParamRef都触发ParamByName的额外逻辑,适合无法修改TParams的场景。

方案3:减少不必要的ParamRef调用

检查业务代码中是否存在重复调用ParamRef的情况,比如循环内反复调用。可以提前把ParamRef的结果缓存到局部变量,减少查找次数:

// 优化前:循环内重复调用ParamRef
for I := 0 to LargeParamList.Count - 1 do
  Query.Params[I].ParamRef.AsString := DataList[I];

// 优化后:提前缓存结果
var
  TargetParam: TParam;
begin
  for I := 0 to LargeParamList.Count - 1 do
  begin
    TargetParam := Query.Params[I].ParamRef;
    if Assigned(TargetParam) then
      TargetParam.AsString := DataList[I];
  end;
end;

内容的提问来源于stack exchange,提问作者user219760

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:12:06