【问题标题】:Delphi clientdataset: speeding up removing records from one data set based on anotherDelphi clientdataset:加快基于另一个数据集从一个数据集中删除记录
【发布时间】:2023-03-17 22:36:02
【问题描述】:

目前我有两个数据集。一个人拥有一组工作所需的所有文件;另一个有文件存在。我需要缺少的要求,即第一个数据集中不在第二个数据集中的文档。 (底层 SQL 数据结构中的问题阻止只查询丢失的文档而不使用过慢的子查询)。

我已尝试从所需的文档数据集中删除存在的文档,因此它只包含缺失的内容。

try
  with cdsAllDocs do begin
    while not eof do begin
      cdsFilter(cdsRequiredDocs, 'JobID = ' + fieldByName('JobID').AsString + ' AND ID = ' + fieldByName('PREDEFINEDDOCID').AsString);
      while cdsRequiredDocs.RecordCount > 0 do cdsRequiredDocs.Delete;
      next;
    end; // while not eof
  end; // with cdsAllDocs 
finally
  cdsFilterClear(cdsRequiredDocs);
end;

我没有费心禁用控件,因为这两个数据集都没有任何关联的控件。

问题是这种方法非常慢。在需要约 7000 个文件的情况下,此过程需要约 2.25 秒才能获得我的精简列表。

有没有办法以更有效的方式做我想做的事情? (除了修复目前我无法控制的 SQL 数据)。谢谢。

以下几个cmets的添加: 一些人建议预先进行更好的查询可以获得单个数据集并首先避免此问题:以下是实际查询: 这个得到了所有要求的列表:

SELECT distinct
  J.JobID,
  P.PREDEFINEDDOCID as ID
FROM JOBTEMPLATECONTEXT JT
Join DEPARTMENTJOB DJ
  on DJ.jobid = JT.JobID
  and DJ.DepartmentJobFetch = "Y"
  and UPPER(DJ.DEPARTMENTJOBSTATUS) in ("ACTIVE", "PENDING")
Join Job J
  on JT.JOBID = J.JOBID
  and J.JOBFETCH = "Y"
Join TEMPLATECONTEXT T
  on T.TEMPLATECONTEXTID = JT.TEMPLATECONTEXTID
  and JT.CONTEXTDUEDATE < "Now"
join PREDEFINEDDOC P on P.PREDEFINEDDOCID = T.TEMPCONTEXTID

而这个(非常慢)获取那里的文档:

SELECT distinct
  J.JobID, P.PREDEFINEDDOCID
FROM Job J
Join JOBTEMPLATECONTEXT JT
  on JT.jobid = J.JobID
  and J.JobFetch = "Y"
Join DEPARTMENTJOB DJ
  on DJ.JOBID = J.JOBID
  and DJ.DepartmentJobFetch = "Y"
  and UPPER(DJ.DEPARTMENTJOBSTATUS) in ("ACTIVE", "PENDING")
Join TEMPLATECONTEXT T
  on T.TEMPLATECONTEXTID = JT.TEMPLATECONTEXTID
join PREDEFINEDDOC P on P.PREDEFINEDDOCID = T.TEMPCONTEXTID
join JOBDOCUMENTS JD
  on JT.JOBID = JD.JOBID
join DOCUMENTS D
  on D.PredefinedDocID = P.PREDEFINEDDOCID
  and D.DOCUMENTFETCH = "Y"
  and JD.DOCUMENTID = D.DOCUMENTID

请不要告诉我在 varChars 而不是枚举类型中存储我所限制的值是不好的,或者表的混乱是不好的数据库设计;我无法控制。

【问题讨论】:

  • 我仍然会进行适当的初始查询。你能告诉我们数据库的类型、表结构和期望的结果吗?也许有人可以制定一个性能更好的查询。
  • @Eric 那么您在客户端数据集上显示的内容是:JobID = JobID 和 ID = PredefinedDocID 在表本身上是不可能的?是否有一些基于数据库外部数据的计算字段?
  • 每个文档是否有唯一的数字文档 ID?我之所以问,是因为我想知道您是否以正确的方式进行此操作:在紧密循环中使用 FieldByNameRecordCount 似乎不建议这样做。
  • 所以我们的目标是让这样的东西以可接受的性能运行:SELECT distinct J.JobID, P.PREDEFINEDDOCID as ID FROM JOBTEMPLATECONTEXT JT ... /* omitted for lack of space */ left outer join JOBDOCUMENTS JD on JT.JOBID = JD.JOBID left outer join DOCUMENTS D on D.PredefinedDocID = P.PREDEFINEDDOCID and D.DOCUMENTFETCH = "Y" and JD.DOCUMENTID = D.DOCUMENTID where D.DOCUMENTID IS NULL
  • 我感受到了你的痛苦。 :-/ 因此,如果您完全无法在客户端上处理它,您可以尝试在客户端数据集上创建一些索引。也正如@MartynA 所说,避免循环中的 FieldByName (在循环之前初始化本地 TField 变量)。

标签: delphi delphi-2010


【解决方案1】:

而不是使用过滤器将 IndexFieldNames 设置为每个数据集的 JobID。然后,您可以遍历每个数据集一次。这比应用过滤器数千次要快得多。

var
  RequiredKey: string;
  AllKey: string;
  // I'm also assuming that Fields were created on the CDS - i.e. cdsRequiredDocsJobId, etc.


cdsRequiredDocs.IndexFieldNames := 'JobID;ID'; 
cdsAllDocs.IndexFieldNames := 'JobID;PREDEFINEDDOCID';

cdsRequiredDocs.First;
cdsAllDocs.First;

while (not cdsRequiredDocs.Eof) and (not cdsAllDocs.Eof) do 
begin
  RequiredKey := cdsRequiredDocsJobId.AsString + cdsRequiredDocsId.AsString;
  AllKey := cdsAllDocsJobId.AsString + cdsAllDocsPreDefinedDocId.AsString;

  // Move through AllDocs until we find a key that is equal or greater 
  // to the one in RequiredDocs
  while AllKey < RequiredKey do
  begin
    cdsAllDocs.Next;
    AllKey := cdsAllDocsJobId.AsString + cdsAllDocsPreDefinedDocId.AsString;      
    if cdsAllDocs.Eof then  
      break;
  end;

  // If we stopped on a key that matches then we need to 
  // delete our row, otherwise we move to the next one to check
  if AllKey = RequiredKey then
  begin
    cdsRequiredDocs.Delete;
    // We will be on the next record already - unless this was the 
    // last record in the CDS, then we will move back one. This can cause an 
    // extra pass through the loop, but since we only delete when the key matches
    // it should not cause anything to be removed that should stay
  end
  else
  begin
    cdsRequiredDocs.Next;
  end;

end; // while not eof

【讨论】:

    【解决方案2】:

    使用设置范围(需要设置索引)比应用过滤器快得多。如果您知道只有一行匹配,您也可以使用 GotoKey 而不是 SetRange。

    如果您将 FieldByName 调用替换为在循环之前设置的 Field 对象(通过将它们创建为 CDS 的一部分或在循环之前将它们保存下来),您也会加快速度。

    try
      cdsRequiredDocs.IndexFieldNames := 'JobId;ID';
    
      with cdsAllDocs do begin
        while not eof do begin
          cdsRequiredDocs.SetRange([fieldByName('JobID').AsString, fieldByName('PREDEFINEDDOCID').AsString],
                                   [fieldByName('JobID').AsString, fieldByName('PREDEFINEDDOCID').AsString]);
    
          while cdsRequiredDocs.RecordCount > 0 do cdsRequiredDocs.Delete;
          next;
        end; // while not eof
      end; // with cdsAllDocs 
    finally
      cdsRequiredDocs.CancelRange;
    end;
    

    【讨论】:

      猜你喜欢
      • 2020-06-26
      • 1970-01-01
      • 2011-11-20
      • 2013-09-13
      • 2011-02-16
      • 2020-03-25
      • 2021-08-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多