要在执行查询时显示ProgressBar,我使用以下代码:
if not Query1.Prepared then
Query1.Prepare;
Query1.Open;
ProgressBar1.Max := Query1.RecordCount;
ProgressBar1.Min := 0;
ProgressBar1.Position := 0;
Query1.First;
while not Query1.Eof do
begin
ProgressBar1.Position := ProgressBar1.Position+1;
ProgressBar1.StepIt;
Query1.Next;
Application.ProcessMessages;
end;
但ProgressBar的显示仍然冻结(我看不到ProgressBar的进度)。
执行此查询时,如何查看ProgressBar1的进度?
答案 0 :(得分:5)
您没有在查询上运行进度。查询在调用Open
时发生。当查询被发送到数据库时,该数据库又处理它,花费时间。
一旦查询完成并迭代结果,您的尝试就会在稍后进行。这可能是如此之快,以至于进步是不必要的。无论如何,如果耗时,至少它会在您的代码中发生,因此您可以显示进度。
您遇到的真正问题是查询发生在数据库服务器上的其他位置,并且您无法从中获取进度回调。我确定某些数据库服务器确实提供了进度回调,但我不认为您的技术可用。
也许最简单的事情是:
答案 1 :(得分:0)
某些数据库组件提供类似TADOxxx组件的异步操作,这样您就可以在查询运行时显示进度。以下是如何使用aysnc模式的完整示例(此示例使用SQL服务器):
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Data.Win.ADODB, Vcl.StdCtrls;
type
TDataSetWrapper = class(TDataSet);
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
Button1: TButton;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
AbortQuery : Boolean;
procedure ADOQuery1FetchProgress(DataSet: TCustomADODataSet; Progress, MaxProgress: Integer; var EventStatus: TEventStatus);
procedure ADOQuery1FetchComplete(DataSet: TCustomADODataSet; const Error: Error; var EventStatus: TEventStatus);
procedure ProcessRecords;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// warning about OnFetchxxxx events, they occurr OUTSIDE the main thread, always synchronize!!!
procedure TForm1.ADOQuery1FetchComplete(DataSet: TCustomADODataSet; const Error: Error; var EventStatus: TEventStatus);
begin
if EventStatus = esOK then
// access your records here
TThread.Synchronize(nil, ProcessRecords);
TThread.Synchronize(nil, procedure()
begin
Button1.Caption := 'Start';
Button1.Tag := 0;
end
);
end;
procedure TForm1.ADOQuery1FetchProgress(DataSet: TCustomADODataSet; Progress, MaxProgress: Integer; var EventStatus: TEventStatus);
begin
// query has been aborted
if Button1.Tag=2 then
begin
EventStatus := esCancel;
TThread.Synchronize(nil, procedure()
begin
if Assigned(ADOQuery1.RecordSet) then ADOQuery1.RecordSet.Cancel;
Button1.Tag := 0;
end
);
end else
// query busy
if Button1.Tag=1 then
TThread.Synchronize(nil, procedure()
begin
// indicate progress
Memo1.Lines.Add(Format('progress: %d',[Progress]));
end
);
end;
procedure TForm1.ProcessRecords;
begin
ADOQuery1.First;
Memo1.Lines.Add(ADOQuery1.Fields[0].AsString);
ADOQuery1.Next;
Memo1.Lines.Add(ADOQuery1.Fields[0].AsString);
ADOQuery1.Next;
Memo1.Lines.Add(ADOQuery1.Fields[0].AsString);
ADOQuery1.Next;
Memo1.Lines.Add(ADOQuery1.Fields[0].AsString);
ADOQuery1.Next;
Memo1.Lines.Add(ADOQuery1.Fields[0].AsString);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// use tag to define state we are in
// 0 means idle
// 1 means query active
// 2 means abort query
if Button1.Tag = 0 then
begin
Button1.Tag := 1;
// simulate long query with cross join, make sure your table has enough records (in this case 1000 records is enough)
ADOQuery1.SQL.Text := 'SELECT TOP 100000 T1.* FROM MyTable T1, MyTable T2';
ADOQuery1.Open;
Button1.Caption := 'Abort';
end else
if Button1.Tag = 1 then
begin
// abort!!!
Button1.Tag := 2;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.Caption := 'Start';
Button1.Tag := 0;
ADOQuery1.ExecuteOptions := [eoAsyncExecute, eoAsyncFetchNonBlocking];
ADOQuery1.OnFetchProgress := ADOQuery1FetchProgress;
ADOQuery1.OnFetchComplete := ADOQuery1FetchComplete;
ADOConnection1.Connected := True;
end;
end.