【问题标题】:Fast SQL query but slow result retrievalSQL 查询快但结果检索慢
【发布时间】:2012-02-29 23:36:16
【问题描述】:

我正在使用 Sybase 的 Advantage 数据库服务器,目前有一个不错的快速左连接查询,运行速度非常快。问题是在运行查询后我想将结果放入一个字符串中。我检索了一个包含 55000 个条目的数据集。 现在最多需要 16 秒。将其放入字符串中。我的查询只用了 8 毫秒即可运行。我的第一次尝试是这样的:

    aADSQuery.Open
    aADSQuery.First
    WHILE not aADSQuery.eof do
    begin
       s := s + aADSQuery.FieldbyName('Name').asString+',';
       aADSQuery.Next;
    end;

之后,我尝试这样做以避免 aADSQuery.next,但 aADSQuery.RecordCount 占用了 我 9 秒。

    aADSQuery.Open
    aADSQuery.First
    Count := aADSQuery.RecordCount;
    for i:=0 to count-1 do
    begin
      aADSQuery.RecNo := i;
      aADSQuery.FieldbyName('Name').AsString; 
    end;

数据库被索引,条目 ID 的主键和其他列的索引。 我考虑过创建一个视图来计算我的条目以避免记录计数,这可能与 sql 计数完全相同。但是视图中条目的计数与以前一样。如果我使用 sql 依靠我的基表有 130000 个条目,它只需要 200 毫秒。但是,如果我对结果表进行计数,而不使用视图,我需要 9 秒。我怀疑是这样,因为新的临时结果表没有索引。有谁知道如何以适当的方式处理此类问题或如何获得更快的结果计数?

非常感谢

【问题讨论】:

  • 您是否尝试过在 ADS 工具中提取所有记录以比较总提取时间?
  • MySQL 有一个 GROUP_CONCAT 函数可以完全满足您的要求,也许 Advantage DB 包含一个类似的函数...
  • @mjn ADS 的 Avantage 数据架构师在 7 毫秒内为我提供了完整的结果表(55000 行)。这实际上几乎是执行我的 sql 查询的时间。我的问题是,ADS 在展示其结果方面有何不同。会不会是某种缓存问题?
  • @André ADS 真的在 7 毫秒内获取并显示(滚动)所有 55K 行在您的屏幕上吗?我猜这一次只针对显示的第一个结果集记录。
  • @mjn 是的,可能是这样。我猜这些可能是前 300 个条目之类的。好吧,也许我只是想从中得到很多

标签: delphi advantage-database-server


【解决方案1】:

使用一些基于缓冲区的类,例如TStringStream 来填充字符串。这将避免 String 连接 (s := s + foo) 的缓慢重新分配。

不要在循环中使用aADSQuery.FieldbyName('Name').AsString。它很慢。 而是像这样创建一个局部变量F

var
  F: TField;

F := aADSQuery.FieldbyName('Name');
for i:=0 to count-1 do
begin
  aADSQuery.RecNo := i;
  F.AsString; 
end;

我相信使用aADSQuery.Next 比使用RecNo 更快

procedure Test;
var
  F: TField;
  Buf: TStringStream;
  S: string;
begin
  aADSQuery.DisableControls;
  try
    aADSQuery.Open;
    F := aADSQuery.FieldbyName('Name');
    Buf := TStringStream.Create('');
    try
      while not aADSQuery.Eof do
      begin
        Buf.WriteString(F.AsString + ',');
        aADSQuery.Next;
      end;
      S := Buf.DataString;
    finally
      Buf.Free;
    end;
  finally
    aADSQuery.EnableControls;
  end;
end;

您可以在服务器端生成该字符串并将其返回给客户端,而无需在客户端构造任何字符串:

DECLARE @Names NVARCHAR(max)
SELECT @Names = ''
SELECT @Names = @Names + ',' + ISNULL([Name], '') FROM MyTable
SELECT @Names

您还可以通过设置TAdsQuery.AdsTableOptions 来优化性能。确保将AdsFilterOptions 设置为IGNORE_WHEN_COUNTING,并将AdsFreshRecordCount 设置为False

【讨论】:

  • +1 用于使用本地字段变量!但是 Delphi 中的字符串分配速度慢吗?在一篇关于 TStringBuilder 的文章中,我读到它实际上非常快(比 TStringBuilder.Add 快)
  • @mjn, TStringBuilder 是在 Delphi 2009 AFAIK 中引入的。我正在使用 D5/7,所以我不熟悉它。我猜它是像TStringStream 这样的缓冲基类。根据我的经验,s := s + foo 等字符串的串联速度很慢。
  • @kobik 谢谢你很好的回答。我现在更改了我的源代码。与 ADS Architect 相比,它仍然非常慢。我得到了 6 秒,而架构师需要 7 毫秒。 (可能会多一点来显示结果,但感觉要快得多)
  • @André Dziurla,ADS 架构师可能正在异步获取结果。所以你会在 7 毫秒后看到第一个结果集。它可能正在使用服务器端游标,因此它在 7 毫秒后完成,并且在您向下滚动网格时获取其余记录。另一方面,您正在使用同步查询,将所有 55000 发送到客户端。并加上收缩字符串。
  • 请注意,在 Delphi 2009 之前,TStringStream 并不比字符串连接快。自 Delphi 2009 以来,它将使用内部编码模式......所以 TStringStream 不是那么快!
【解决方案2】:

你为什么不执行你需要的服务器端并返回结果?

而且在这种情况下:

  • 您每次都强制 Delphi 重新分配字符串。如果您知道它可以达到的最大大小,您应该在之前预先分配字符串大小,然后在完成时“更正”它。
  • FieldByName() 很慢 - 它必须为每次调用执行查找。定义您要用于数据集的字段。
  • 如果记录“大”,网络性能可能很重要。您可以将 MTU 设置为更大的值,以更好地利用当今的千兆网络。

【讨论】:

  • 我只是使用本地服务器;
【解决方案3】:

从 OP 来看,我并不完全清楚目标是查找记录总数还是向用户显示数据。如果要显示数据,那么将 55,000 条记录的所有数据附加到单个字符串中可能不是最好的方法。如果您对在 Advantage Data Architect 中运行查询的性能感到满意,那么使用类似的方法并将数据存储在某种网格中可能是有意义的。

例如,将 TDataSource 与 TDBGrid 关联并将查询绑定到数据源:

AdsQuery1.Open;
DataSource1.DataSet:=AdsQuery1;
DBGrid1.DataSource:=DataSource1;

数据感知网格只会获取填充网格所需的数据,并会在用户翻页时按需请求数据。

编辑当您请求记录计数时,整个结果集必须由服务器解析。如果您使用 Advantage Local Server 并且数据驻留在网络服务器上,那么通过网络读取所有数据将会产生额外成本。如果您使用 Advantage 数据库服务器(客户端/服务器版本),则处理将在服务器上进行,并且可能更快。

这当然取决于查询,但解析结果集的 9 秒可能太长了。在 Advantage Data Architect 中,您可以检查查询的优化。 SQL 菜单下有一个“显示计划”选项,SQL 实用程序的工具栏上有一个按钮,用于显示查询计划。可能是您缺少必要的索引。

【讨论】:

  • 我想做的是获取我的结果的数量,并以最快的方式从一个条目移动到下一个条目,也许是在一个 for 循环中。如果可能的话,我想通过一个 DBGrid 来避免这一步。我的问题是,我在 OP 中怎么说 ADSQuery.RecCount 非常慢。
  • 执行查询只需要 8 毫秒。我的连接和其他一切都直接在服务器上。通过我的一张表进行 130 k 条目的 sql 计数需要 200 毫秒。查询计划还可以,没有优化问题或其他任何问题。
  • 只是结果数据集有问题。我在两个表上使用左外连接。
  • @AndréDziurla:最初的执行成本只是解析、语义检查等,然后它返回第一行(或者可能是前几行……我忘了)。所以它通常非常快。获取记录数意味着它必须解析整个结果集(如果缓存不适合,可能会将 55,000 条记录写入磁盘)。但是 9 秒确实看起来很慢。您最好开始一个专门询问查询的新问题(显示查询、表定义等)。也许可以做出改进。
【解决方案4】:

所以最后,我发现了我的错误,但有些事情我不明白。 首先,我在连接查询中更改了左连接 sql 查询。这使我的记录计数更快,而且如果我使用下一个,它现在更快。所以在那之后我检查了每一列的表格类型。而且我发现,如果没有必要,对字符列使用固定大小的性能不是很好。在我的例子中,我为 20 列选择了 100 的大小,但是我的列的大小从第 1 列增加到 20 列,分三步。所以第 20 列的最大大小是 60,第 1 列有 3 个字符。(这些是我的搜索列)这使我的 while do 子句已经快了两倍。随着这些变化。我可以在 5500 毫秒内获得我的 55000 个条目。现在我改变了桌子设计。我已经把所有东西都放在了一张桌子上,我不再需要加入了。至少暂时不会。我使用了普通的 Select .. From .. Where 子句。我获得这 55000 个条目结果的时间再次减少到 2500 毫秒。这对我来说很好。 所以唯一的问题仍然是,如果我使用不同的 sql 查询,为什么在执行 ADSQuery.open 之后获取数据会有很大的不同。我认为这可能会影响sql查询的执行时间,但它也影响了 结果获取。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-29
    • 2021-12-13
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 2011-11-01
    相关资源
    最近更新 更多