执行查询时显示ProgressBar

时间:2016-12-03 06:55:24

标签: delphi

要在执行查询时显示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的进度?

2 个答案:

答案 0 :(得分:5)

您没有在查询上运行进度。查询在调用Open时发生。当查询被发送到数据库时,该数据库又处理它,花费时间。

一旦查询完成并迭代结果,您的尝试就会在稍后进行。这可能是如此之快,以至于进步是不必要的。无论如何,如果耗时,至少它会在您的代码中发生,因此您可以显示进度。

您遇到的真正问题是查询发生在数据库服务器上的其他位置,并且您无法从中获取进度回调。我确定某些数据库服务器确实提供了进度回调,但我不认为您的技术可用。

也许最简单的事情是:

  1. 将查询代码移动到单独的线程中。这对于保持主UI线程响应至关重要。
  2. 您可以使用选取框进度条,而不是尝试填充由于您没有进度回调而无法执行的进度条。这是进度条来回移动以告诉用户程序正忙,但没有挂起。

答案 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.