ADOQuery.Locate慢,创建索引

ADOQuery.Locate Slow, Create Index

我有以下行来在查询中定位一行。

if Query.Locate('Line;Hour;Minute',VarArrayOf([Line-400,AHour,minuteof(Start)]),[]) = true then

这很慢,现在我记得可以将索引添加到查询中,这样定位本身就会快好几倍。不幸的是我似乎找不到这个例子。

有人可以帮帮我吗? 问候 罗伯特

文档说 Locate 将光标移动到匹配特定搜索条件的第一行
如果你的 table 有很多记录,Locate 是 slow.
Locate 通常用于本地数据库,但在客户端-服务器 RDBM 中,最好使用 SQLWHERE 来最小化搜索时间并尽量减少数据流量。

有趣的问题。

更新请参阅下面的更新。

我在我的 SS2014 Sql 服务器上设置了一些测试数据,以便 运行 使用如下代码进行一些测试:

ID := 1;
for Line := 1 to 1000 do begin
  for AHour := 1 to 24 do begin
    for AMinute := 1 to 60 do begin
      AdoQuery1.InsertRecord([ID, Line, AHour, AMinute]);
      Inc(ID);
      end;
    end;
  end;
end;

然后,我运行像这样的一些测试

procedure TForm1.LocateTest1(DisableControls, UseSort : Boolean);
var
  T1 : Integer;
  Line,
  AHour,
  AMinute : Integer;
begin

  AdoQuery1.Sql.Text := 'select * from linetest order by line, ahour, aminute';
  AdoQuery1.CursorLocation := clUseClient;
  AdoQuery1.Open;
  T1 := GettickCount;
  if DisableControls then
    AdoQuery1.DisableControls;

  if UseSort then
    AdoQuery1.Recordset.Sort := 'Line,AHour,AMinute';
  Line := 1000;
  AHour := 23;
      for AMinute := 60 downto 1 do begin
        if not AdoQuery1.Locate('Line;AHour;AMinute', VarArrayOf([Line, AHour, AMinute]), []) then
          Caption := Format('Locate failed %d %d %d', [Line, AHour, AMinute]);
      end;
  Memo1.Lines.Add('Test1 : ' + IntToStr(GetTickCount - T1));
  if DisableControls then
    AdoQuery1.EnableControls;
  AdoQuery1.Close;
end;

之所以涉及Disable/EnableControls是因为我上报的结果 这里 ,即使没有 db-aware,调用 DisableControls 也会对滚动速度产生巨大影响 涉及控制。

但是,滚动似乎不会对在 TAdoQuery 上执行 Locate() 产生重大影响,因为调用 DisableControls 只花费了大约 1.5 秒的记录时间 26 秒。显然,TAdoQuery.Locate 对大量行的表现并不好。

UseSort参数的思路是看是否对RecordSet后面的排序 AdoQuery 对速度有任何影响,但没有,原因是 Locate 调用 TCustomAdoDataSet.LocateRecord 无论如何都使用排序。

您提到了添加索引。不幸的是,TAdoQuery 只支持使用服务器端索引 在执行 thq SQL 查询时,未在检索的结果集中定位记录。您可以添加 客户端索引到 TAdoTable,但根据与上述类似的测试,令我惊讶的是, 他们做了 Locate() 的速度几乎没有差异。

所以,鉴于我目前的结果,使用参数化的方法似乎可能更快 SELECT 只检索当前感兴趣的行,而不是尝试定位它 在一个大的测试集中。 Alte运行taivel,您可以通过 DatasetProvider 或 FireDAC FDMemTable 等将结果集检索到 ClientDataSer 中。Ymmv,这取决于您在做什么...

更新 由于 post 是我原来的答案,我有一些进一步的更新 包括在内可能会有用。

  • 一种方法是通过调用 AdoQuery 的 RecordSet 的 Find 和 Filter 方法来模仿 Locate,这种方法比重复执行 AdoQuery1.Locate 要快得多(大约 15 秒)。我仍在解决这个问题,一两天内将 post 再次更新。

  • 另一个是简单地提到执行 Locates 执行 FireDAC FDQuery 而不是 AdoQuery。这似乎在 9 秒内使用 AdoQuery 执行了大约 25 秒的同一组定位,使用以下代码:

使用FDQuery.Locate

procedure TForm2.LocateTest;
var
  T1 : Integer;
  Line,
  AHour,
  AMinute : Integer;
begin

  FDQuery1.Sql.Text := 'select * from linetest order by line, ahour desc, aminute desc';
  //FDQuery1.CursorLocation := clUseClient;
  FDQuery1.CursorKind := ckForwardOnly;
  FDQuery1.Open;

  T1 := GettickCount;
  Line := 1000;
  AHour := 1;

  for AMinute := 1 to 60 do begin
    if not FDQuery1.Locate('Line;AHour;AMinute', VarArrayOf([Line, AHour, AMinute]), []) then
      Caption := Format('Locate failed %d %d %d', [Line, AHour, AMinute]);
  end;

  Memo1.Lines.Add('Test1 : ' + IntToStr(GetTickCount - T1));
  FDQuery1.Close;
end;