5

热门(抱歉)答案,适用于那些没有时间进入但可能有类似问题的人。

规则#1,一如既往,尽可能多地移出循环。
2、将 TField var := ADODataSet.FieldByname() 移出循环 3、ADODataSet.DisableControls(); 和 ADODataSet.EnableControls(); 在循环 4 周围,每行上的 stringGrid.Rows[r].BeginUpdate() 和 EndUpdate() (不能在控制时执行)每个都缩短了几秒钟,我把它归结为“比眼睛快可以看到”通过改变

loop
  stringGrid.RowCount := stringGrid.RowCount + 1;
end loop

放在stringGrid.RowCount := ADODataSet.RecordCount;循环之前

+1 并衷心感谢所有提供帮助的人。

(现在我会去看看我能做些什么来优化绘制 TChart,这也很慢 ;-)


表中有大约 3,600 行,这需要 45 秒来填充字符串网格。我究竟做错了什么?

   ADODataSet := TADODataSet.Create(Nil);
   ADODataSet.Connection := AdoConnection;

   ADODataSet.CommandText := 'SELECT * FROM 测量值';
   ADODataSet.CommandType := cmdText;
   ADODataSet.Open();

   而不是 ADODataSet.eof 做
   开始
      TestRunDataStringGrid.RowCount := TestRunDataStringGrid.RowCount + 1;

      measureDateTime := UnixToDateTime(ADODataSet.FieldByname('time_stamp').AsInteger);
      DoSQlCommandWithResultSet('SELECT * FROM start_time_stamp', AdoConnection, resultSet);
      startDateTime := UnixToDateTime(StrToInt64(resultSet.Strings[0]));
      elapsedTime := 测量日期时间 - 开始日期时间;
      TestRunDataStringGrid.Cells[0, Pred(TestRunDataStringGrid.RowCount)] := FormatDateTime('hh:mm:ss', elapsedTime);
      TestRunDataStringGrid.Cells[1, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('inputTemperature').AsFloat);
      TestRunDataStringGrid.Cells[2, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('outputTemperature').AsFloat);
      TestRunDataStringGrid.Cells[3, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('flowRate').AsFloat);
      TestRunDataStringGrid.Cells[4, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('waterPressure').AsFloat * convert);
      TestRunDataStringGrid.Cells[5, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('waterLevel').AsFloat);
      TestRunDataStringGrid.Cells[6, Pred(TestRunDataStringGrid.RowCount)] := FloatToStrWithPrecision(ADODataSet.FieldByname('cod').AsFloat);
      ADODataSet.Next;
   结尾;

   ADODataSet.Close();
   ADODataSet.Free();

更新:

函数 DoSQlCommandWithResultSet(const command : String; AdoConnection : TADOConnection; resultSet : TStringList): Boolean;
  变量
        我:整数;
        AdoQuery:TADOQuery;

开始
  结果:=真;
  结果集.清除();

  AdoQuery := TADOQuery.Create(nil);
  尝试
    AdoQuery.Connection := AdoConnection;
    AdoQuery.SQL.Add(命令);
    AdoQuery.Open();
    我:= 0;
    而不是 AdoQuery.eof 做
    开始
      resultSet.Add(ADOQuery.Fields[i].Value);
      我 := 我 + 1;
      AdoQuery.Next;
    结尾;

  最后
    AdoQuery.Close();
    AdoQuery.Free();
  结尾;
结尾;

4

6 回答 6

10
  1. 您正在执行该命令SELECT * FROM start_time_stamp3,600 次,但在我看来,它与您的外部循环没有任何关联。为什么不在循环前执行一次呢?

  2. 该 SELECT 命令似乎只返回单条记录的单列,但您使用“*”加载所有列,并且没有 WHERE 子句将结果限制为单行(如果表中有不止一行)。

  3. 您仅使用来自 Measurements 的有限数量的列,但您检索所有带有“*”的列。

  4. 您没有显示 的​​内容DoSQlCommandWithResultSet,因此不清楚该例程是否存在问题。

  5. 目前尚不清楚问题出在您的数据库访问还是字符串网格中。注释掉与字符串网格有关的所有行并运行程序。单独访问数据库需要多长时间?

于 2011-01-22T03:45:16.447 回答
9

除了拉里·勒斯蒂格(Larry Lustig)的观点:

  1. 一般来说,FieldByName 是比较慢的方法。您正在为相同的字段循环调用它。将字段引用的获取移出循环并将引用存储在变量中。像:InputTempField := ADODataSet.FieldByname('inputTemperature');
  2. 您正在调整循环中的网格大小TestRunDataStringGrid.RowCount := TestRunDataStringGrid.RowCount + 1。就是这样,当你应该ADODataSet.RecordCount在循环之前使用:TestRunDataStringGrid.RowCount := ADODataSet.RecordCount.
  3. ADODataSet.DisableControls这是在循环之前和循环之后调用的好习惯ADODataSet.EnableControls。更实际的是 ADO 数据集,它没有最佳实现,这些调用有帮助。
  4. 根据您使用的 DBMS,您可以通过设置更大的“行集大小”来提高获取性能。不确定,它如何在 ADO 中控制,可能将 ADODataSet.CacheSize 设置为更大的值会有所帮助。此外,还有光标设置:)
于 2011-01-22T07:57:49.283 回答
5

而不是在循环内调用 ADODataSet.FieldByname('Fieldname') 您应该为每个字段声明 TField 类型的局部变量,将 ADODataset.FindField('Fieldname') 分配给变量并在循环内使用变量。FindFieldByName 在每次调用时搜索一个列表。

更新:

procedure TForm1.Button1Click(Sender: TObject);
var
  InputTemp, OutputTemp: TField;
begin
  ADODataSet := TADODataSet.Create(Nil);
  try
    ADODataSet.Connection := ADOConnection;
    ADODataSet.CommandText := 'SELECT * FROM measurements';
    ADODataSet.Open;
    InputTemp := ADODataSet.FindField('inputTemperature');
    OutputTemp := ADODataSet.FindField('outputTemperature');
    // assign more fields here
    while not ADODataSet.Eof do begin
      // do something with the fields, for example:
      // GridCell := Format ('%3.2f', [InputTemp.AsFloat]);
      // GridCell := InputTemp.AsString;
      ADODataSet.Next;
    end;
  finally
    ADODataSet.Free;
  end;
end;

另一种选择是将 TADODataset 组件放在表单上(或使用 TDataModule)并在设计时定义字段。

于 2011-01-22T05:48:52.743 回答
2

除了 Larry Lustig 的回答之外,还可以考虑使用data-aware控件,例如替代文字 TDbGrid组件。

于 2011-01-22T04:36:31.217 回答
2

如果您不使用数据感知控件,则应使用TestRunDataStringGrid.BeginUpdatebefore 和TestRunDataStringGrid.EndUpdateafter 循环。如果没有这个,您的网格会在每次修改后不断重绘(添加新行、单元格更新)。

AdoQuery.LockType := ltReadOnly在打开查询之前设置了另一个提示。

于 2011-01-22T09:17:29.533 回答
1

您还可以尝试使用检测分析器而不是采样分析器来获得更好的结果(采样分析器会丢失很多详细信息,并且大多数情况下它们每秒的样本数少于 1000 个,而 1000 已经很低了:只有快速了解一下才好)。

检测分析器:

于 2011-01-24T08:03:09.520 回答