saa*_*stn 10 delphi ms-access ado delphi-xe5
我想从MS Access文件中读取整个表,我正在尝试尽快完成.在测试大样本时,我发现循环计数器在读取与表的最后记录相比的最高记录时增加得更快.这是一个演示此示例的示例代码:
procedure TForm1.Button1Click(Sender: TObject);
const
MaxRecords = 40000;
Step = 5000;
var
I, J: Integer;
Table: TADOTable;
T: Cardinal;
Ts: TCardinalDynArray;
begin
Table := TADOTable.Create(nil);
Table.ConnectionString :=
'Provider=Microsoft.ACE.OLEDB.12.0;'+
'Data Source=BigMDB.accdb;'+
'Mode=Read|Share Deny Read|Share Deny Write;'+
'Persist Security Info=False';
Table.TableName := 'Table1';
Table.Open;
J := 0;
SetLength(Ts, MaxRecords div Step);
T := GetTickCount;
for I := 1 to MaxRecords do
begin
Table.Next;
if ((I mod Step) = 0) then
begin
T := GetTickCount - T;
Ts[J] := T;
Inc(J);
T := GetTickCount;
end;
end;
Table.Free;
// Chart1.SeriesList[0].Clear;
// for I := 0 to Length(Ts) - 1 do
// begin
// Chart1.SeriesList[0].Add(Ts[I]/1000, Format(
// 'Records: %s %d-%d %s Duration:%f s',
// [#13, I * Step, (I + 1)*Step, #13, Ts[I]/1000]));
// end;
end;
Run Code Online (Sandbox Code Playgroud)
该表有两个字符串字段,一个double和一个整数.它没有主键也没有索引字段.为什么会发生这种情况,我该如何预防呢?
Mar*_*ynA 19
我可以使用AdoQuery重现您的结果,其中MS Sql Server数据集的大小与您的相似.
然而,在做了一些线性分析之后,我想我已经找到了答案,这有点违反直觉.我敢肯定,在Delphi中进行数据库编程的每个人都习惯了如果通过调用Disable/EnableControls来环绕循环,那么循环数据集往往要快得多.但是,如果没有数据集附加数据集感知控件,谁会费心去做?
好吧,事实证明,在你的情况下,即使没有DB-aware控件,如果你使用Disable/EnableControls,速度也会大大增加.
原因是AdoDB.Pas中的TCustomADODataSet.InternalGetRecord包含:
if ControlsDisabled then
RecordNumber := -2 else
RecordNumber := Recordset.AbsolutePosition;
Run Code Online (Sandbox Code Playgroud)
根据我的行分析器,而不是AdoQuery1.Eof做AdoQuery1.Next循环花费98.8%的时间执行赋值
RecordNumber := Recordset.AbsolutePosition;
Run Code Online (Sandbox Code Playgroud)
!Recordset.AbsolutePosition的计算当然是隐藏在Recordset接口的"错误的一面",但事实上,调用它的时间显然会增加你进入记录集的进一步,这使得推测它的计算是合理的从记录集的数据开始计数.
当然,ControlsDisabled如果DisableControls已经调用而不是通过调用撤消,则返回true EnableControls.因此,使用Disable/EnableControls包围的循环重新测试,希望你会得到与我类似的结果.看起来你是对的,减速与内存分配无关.
使用以下代码:
procedure TForm1.btnLoopClick(Sender: TObject);
var
I: Integer;
T: Integer;
Step : Integer;
begin
Memo1.Lines.BeginUpdate;
I := 0;
Step := 4000;
if cbDisableControls.Checked then
AdoQuery1.DisableControls;
T := GetTickCount;
{.$define UseRecordSet}
{$ifdef UseRecordSet}
while not AdoQuery1.Recordset.Eof do begin
AdoQuery1.Recordset.MoveNext;
Inc(I);
if I mod Step = 0 then begin
T := GetTickCount - T;
Memo1.Lines.Add(IntToStr(I) + ':' + IntToStr(T));
T := GetTickCount;
end;
end;
{$else}
while not AdoQuery1.Eof do begin
AdoQuery1.Next;
Inc(I);
if I mod Step = 0 then begin
T := GetTickCount - T;
Memo1.Lines.Add(IntToStr(I) + ':' + IntToStr(T));
T := GetTickCount;
end;
end;
{$endif}
if cbDisableControls.Checked then
AdoQuery1.EnableControls;
Memo1.Lines.EndUpdate;
end;
Run Code Online (Sandbox Code Playgroud)
我得到以下结果(除非另有说明,否则不调用DisableControls ):
Using CursorLocation = clUseClient
AdoQuery.Next AdoQuery.RecordSet AdoQuery.Next
.MoveNext + DisableControls
4000:157 4000:16 4000:15
8000:453 8000:16 8000:15
12000:687 12000:0 12000:32
16000:969 16000:15 16000:31
20000:1250 20000:16 20000:31
24000:1500 24000:0 24000:16
28000:1703 28000:15 28000:31
32000:1891 32000:16 32000:31
36000:2187 36000:16 36000:16
40000:2438 40000:0 40000:15
44000:2703 44000:15 44000:31
48000:3203 48000:16 48000:32
=======================================
Using CursorLocation = clUseServer
AdoQuery.Next AdoQuery.RecordSet AdoQuery.Next
.MoveNext + DisableControls
4000:1031 4000:454 4000:563
8000:1016 8000:468 8000:562
12000:1047 12000:469 12000:500
16000:1234 16000:484 16000:532
20000:1047 20000:454 20000:546
24000:1063 24000:484 24000:547
28000:984 28000:531 28000:563
32000:906 32000:485 32000:500
36000:1016 36000:531 36000:578
40000:1000 40000:547 40000:500
44000:968 44000:406 44000:562
48000:1016 48000:375 48000:547
Run Code Online (Sandbox Code Playgroud)
AdoQuery1.Recordset.MoveNext当然,将调用直接调用到MDac/ADO层,而AdoQuery1.Next涉及标准TDataSet模型的所有开销.正如Serge Kraikov所说,改变CursorLocation肯定会有所不同,并没有表现出我们注意到的减速,但显然它比使用clUseClient并调用DisableControls要慢得多.我想这取决于你是否可以利用与RecordSet.MoveNext一起使用clUseClient的额外速度.