Tôi có thể sao chép kết quả của bạn bằng cách sử dụng AdoQuery với tập dữ liệu MS Sql Server có kích thước tương tự như của bạn.
Tuy nhiên, sau khi thực hiện một chút dòng định dạng, tôi nghĩ rằng tôi đã tìm thấy câu trả lời cho điều này và nó hơi phản trực giác. Tôi chắc rằng tất cả những ai thực hiện lập trình DB trong Delphi được sử dụng cho ý tưởng rằng việc lặp qua một tập dữ liệu có xu hướng nhanh hơn nhiều nếu bạn bao quanh vòng lặp bằng các cuộc gọi tới Disable/EnableControls. Nhưng ai sẽ làm phiền điều đó nếu không có các điều khiển nhận biết db gắn liền với tập dữ liệu?
Vâng, nó chỉ ra rằng trong tình huống của bạn, mặc dù không có điều khiển nhận thức DB, tốc độ tăng lên rất nhiều nếu bạn sử dụng Disable/EnableControls bất kể.
Lý do là TCustomADODataSet.InternalGetRecord trong AdoDB.Pas chứa này:
if ControlsDisabled then
RecordNumber := -2 else
RecordNumber := Recordset.AbsolutePosition;
và theo hồ sơ dòng của tôi, trong khi không AdoQuery1.Eof làm AdoQuery1.Next loop dành 98,8% thời gian của mình thực hiện bài tập
RecordNumber := Recordset.AbsolutePosition;
! Các tính toán của Recordset.AbsolutePosition được ẩn, tất nhiên, trên "bên sai" của giao diện Recordset, nhưng thực tế là thời gian để gọi nó dường như tăng hơn nữa bạn đi vào recordset làm cho nó hợp lý imo để suy đoán rằng nó được tính toán bằng cách đếm từ đầu dữ liệu của recordset.
Tất nhiên, ControlsDisabled
trả về true nếu DisableControls
đã được gọi và không được hoàn tác bằng một cuộc gọi đến EnableControls
. Vì vậy, hãy thử lại với vòng lặp được bao quanh bởi Disable/EnableControls và hy vọng bạn sẽ nhận được một kết quả tương tự như của tôi. Có vẻ như bạn đã đúng rằng sự chậm lại không liên quan đến phân bổ bộ nhớ.
Sử dụng đoạn mã sau:
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;
tôi nhận được kết quả như sau (với DisableControls không gọi là trừ khi có ghi chú):
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
Calling AdoQuery1.Recordset.MoveNext
cuộc gọi trực tiếp vào lớp MDAC/ADO , trong số khóa học, trong khi AdoQuery1.Next liên quan đến tất cả chi phí của mô hình TDataSet tiêu chuẩn. Như Serge Kraikov đã nói, việc thay đổi CursorLocation chắc chắn tạo ra sự khác biệt và không thể hiện sự chậm lại mà chúng ta nhận thấy, mặc dù rõ ràng nó chậm hơn đáng kể so với việc sử dụng clUseClient và gọi DisableControls. Tôi cho rằng nó phụ thuộc vào chính xác những gì bạn đang cố gắng để làm cho dù bạn có thể tận dụng lợi thế của tốc độ thêm của việc sử dụng clUseClient với RecordSet.MoveNext.
Không. Tôi đang tạo điều khiển theo chương trình, không có gì nhiều hơn những gì bạn có thể thấy trong mã mẫu. – saastn
Không phải là vòng lặp For của bạn không? Dù sao, bạn có ngạc nhiên rằng nếu bạn đọc rất nhiều hồ sơ, nó liên quan đến rất nhiều phân bổ bộ nhớ, và rằng những mất nhiều thời gian hơn bộ nhớ được phân bổ? – MartynA
@MartynA Bạn nói đúng về vòng lặp. Nhưng tôi không thể nói rằng đó là phân bổ bộ nhớ mà làm cho nó chậm hơn. Có vẻ như nó nạp tất cả các bản ghi trong 'Table.Open', Trình quản lý Tác vụ sẽ không hiển thị cấp phát bộ nhớ sau khi chạy dòng đó. – saastn