Delphi Programming / Object Pascal
[掲載 2009年05月24日] [更新 2013年12月12日] エクセル操作フォームEx |
エクセル操作フォームEx
テストプログラム DBTest |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE(UP1) Pro + Excel 2010 |
Delphi 付属の DBDEMOS のデータを読込んでエクセルに表示します.単純にセルに書込んでいくコードと,バリアント配列を使用した場合,Range オブジェクトの CopyFromRecordset メソッドを使用した場合を比較してみました.実行時間を計測して表示します.
筆者の環境では,バリアント配列を使用した場合,約 20 ~ 30 倍の速度となりました.
エクセルのグリッドには,TDBGird の機能はありません.表示したデータを更新するには,セル値の変更イベント等で,データベースの Field に値をセットする必要があります (リアルタイムに処理する場合).これは,データの表示に TStringGrid を使用する場合と同じです.
エクセルの処理速度の比較,データベースの関係については,以下にも記事があります. |
|
図1
設計時仮面
|
|
図2
実行結果 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses DBTestUnit;
{$R *.dfm}
//=============================================================================
// エクセル操作フォームEx(の継承フォーム)をモーダル表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
plExcelFormEx2.ShowModal;
end;
end.
unit DBTestUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Buttons, StdCtrls, plExcelEx, ToolWin, ComCtrls, ExtCtrls, DB,
ADODB;
type
TplExcelFormEx2 = class(TplExcelFormEx)
Button1: TButton;
Button2: TButton;
Memo1: TMemo;
Memo2: TMemo;
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
Button3: TButton;
Memo3: TMemo;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
plExcelFormEx2: TplExcelFormEx2;
implementation
uses MMSystem;
{$R *.dfm}
//=============================================================================
// Formを表示する時の処理
//=============================================================================
procedure TplExcelFormEx2.FormShow(Sender: TObject);
begin
//スクロール範囲
xlfScrollArea := 'A1:H80';
//起動時に表示するブックの指定
xlfBookFullPath := ExtractFilePath(Application.ExeName) + 'DBTestBook.xls';
inherited;
end;
//=============================================================================
// 単純にセルにデータを代入する方法
// xlfExcelSheet.Cellsを,OleVariantでなく,Rangeで定義するとコード補完機能が利
// 用できて便利.速度的にも有利.
//=============================================================================
procedure TplExcelFormEx2.Button1Click(Sender: TObject);
var
DBPath : String;
ConnectStr : String;
RecCnt : Integer;
i : Integer;
j : Integer;
StartRow : Integer;
StartRowStr : String;
EndRowStr : String;
TStart : DWORD;
TEnd : DWORD;
ARange : Txlf2000Range;
begin
//ADO関係の設定
//まずデータベースファイルの場所をDBPathに代入
DBPath := GetEnvironmentVariable('CommonProgramFiles');
{$IF CompilerVersion > 18.49}
DBPath := DBPath + '\CodeGear Shared\Data\dbdemos.mdb';
{$ELSE}
DBPath := DBPath + '\Borland Shared\Data\dbdemos.mdb';
{$IFEND}
ADOConnection1.Connected := False;
//接続文字列の作成(パスワードなしの場合)
ConnectStr := 'Provider=Microsoft.Jet.OLEDB.4.0';
ConnectStr := ConnectStr + ';Data Source=' + DBPath;
ADOConnection1.ConnectionString := ConnectStr;
ADOConnection1.Mode := cmReadWrite;
ADOQuery1.Connection := ADOConnection1;
//クエリー実行
//RecCntがレコード数.ADOQuery1.RecordCountでも取得可能
try
with ADOQuery1 do begin
Close;
SQL.Clear;
SQL.Add('select * from customer');
Open;
end;
except
exit;
end;
//レコード数
RecCnt := ADOQuery1.RecordCount;
//書込み範囲のセルをクリア
StartRow := 7;
StartRowStr := 'C' + IntToStr(StartRow);
EndRowStr := 'F' + IntToStr(StartRow + RecCnt - 1);
xlfClearRange(StartRowStr,EndRowStr);
LockWindowUpdate(PanelLeft.Handle);
Screen.Cursor := crHourGlass;
//開始時刻
TStart := TimeGetTime;
ARange := xlfExcelSheet.Cells;
//3回実行する
with ADOQuery1 do begin
for j := 0 to 2 do begin
First;
for i:=0 to RecCnt-1 do begin
ARange.Item[i+7,3].Value := FieldByName('CustNo').AsInteger;
ARange.Item[i+7,4].Value := FieldByName('Company').AsString;
ARange.Item[i+7,5].Value := FieldByName('Addr1').AsString;
ARange.Item[i+7,6].Value := FieldByName('LastInvoiceDate').AsDateTime;
Next;
end;
end;
Close;
end;
//終了時刻
TEnd := TimeGetTime;
Screen.Cursor := crDefault;
LockWindowUpdate(0);
//実行時間を表示
Memo1.Text:=IntToStr(TEnd-TStart) + ' ms ';
//保存したことにする(つまり変更なし)
xlfBookSaved := True;
end;
//=============================================================================
// バリアント配列を使用する方法
// 非常に高速になる.この例だと約40倍近くいくかも知れない
//=============================================================================
procedure TplExcelFormEx2.Button2Click(Sender: TObject);
var
DBPath : String;
ConnectStr : String;
RecCnt : Integer;
FCnt : Integer;
i : Integer;
j : Integer;
StartRow : Integer;
StartRowStr : String;
EndRowStr : String;
TStart : DWORD;
TEnd : DWORD;
OleArray : OleVariant;
begin
//ADO関係の設定
//まずデータベースファイルの場所をDBPathに代入
DBPath := GetEnvironmentVariable('CommonProgramFiles');
{$IF CompilerVersion > 18.49}
DBPath := DBPath + '\CodeGear Shared\Data\dbdemos.mdb';
{$ELSE}
DBPath := DBPath + '\Borland Shared\Data\dbdemos.mdb';
{$IFEND}
ADOConnection1.Connected := False;
//接続文字列の作成(パスワードなしの場合)
ConnectStr := 'Provider=Microsoft.Jet.OLEDB.4.0';
ConnectStr := ConnectStr + ';Data Source='+ DBPath;
ADOConnection1.ConnectionString := ConnectStr;
ADOConnection1.Mode := cmReadWrite;
ADOQuery1.Connection := ADOConnection1;
//クエリー実行
try
with ADOQuery1 do begin
Close;
SQL.Clear;
SQL.Add('select * from customer');
Open;
end;
except
exit;
end;
//レコード数
RecCnt := ADOQuery1.RecordCount;
//項目の数
FCnt := ADOQuery1.FieldCount;
//書込み範囲のセルをクリア
StartRow := 7;
StartRowStr := 'C' + IntToStr(StartRow);
EndRowStr := 'F' + IntToStr(StartRow + RecCnt - 1);
xlfClearRange(StartRowStr,EndRowStr);
LockWindowUpdate(PanelLeft.Handle);
Screen.Cursor := crHourGlass;
//開始時刻
TStart := TimeGetTime;
//3回実行する
for j := 0 to 2 do begin
//Variant配列を作成.高速化のためにLock
OleArray := VarArrayCreate([0, RecCnt - 1, 0, FCnt -1 ],VarVariant);
VarArrayLock(OleArray);
//先頭から配列にデータを代入
with ADOQuery1 do begin
First;
for i:=0 to RecCnt-1 do begin
OleArray[i,0] := FieldByName('CustNo').AsInteger;
OleArray[i,1] := FieldByName('Company').AsString;
OleArray[i,2] := FieldByName('Addr1').AsString;
OleArray[i,3] := FieldByName('LastInvoiceDate').AsDateTime;
Next;
end;
end;
//エクセルに配列データを送る
xlfExcelSheet.Range[StartRowStr,EndRowStr].Value := OleArray;
//Variant配列をUnLockして空にする
VarArrayUnLock(OleArray);
VarClear(OleArray);
end;
ADOQuery1.Close;
//終了時刻
TEnd := TimeGetTime;
Screen.Cursor := crDefault;
LockWindowUpdate(0);
//実行時間を表示
Memo2.Text := IntToStr(TEnd-TStart) + ' ms ';
//保存したことにする(つまり変更なし)
xlfBookSaved := True;
end;
//=============================================================================
// RangeオブジェクトのCopyFromRecordsetメソッドを使用した例
// バリアント配列による方法に近い速度が期待できる
// Delphiを使う場合,この方法が一番ノーマルな方法かも知れない
//=============================================================================
procedure TplExcelFormEx2.Button3Click(Sender: TObject);
var
DBPath : String;
ConnectStr : String;
RecCnt : Integer;
i : Integer;
StartRow : Integer;
StartRowStr : String;
EndRowStr : String;
TStart : DWORD;
TEnd : DWORD;
begin
//ADO関係の設定
//まずデータベースファイルの場所をDBPathに代入
DBPath := GetEnvironmentVariable('CommonProgramFiles');
{$IF CompilerVersion > 18.49}
DBPath := DBPath + '\CodeGear Shared\Data\dbdemos.mdb';
{$ELSE}
DBPath := DBPath + '\Borland Shared\Data\dbdemos.mdb';
{$IFEND}
ADOConnection1.Connected := False;
//接続文字列の作成(パスワードなしの場合)
ConnectStr := 'Provider=Microsoft.Jet.OLEDB.4.0';
ConnectStr := ConnectStr + ';Data Source=' + DBPath;
ADOConnection1.ConnectionString := ConnectStr;
ADOConnection1.Mode := cmReadWrite;
ADOQuery1.Connection := ADOConnection1;
//クエリー実行
//RecCntがレコード数.ADOQuery1.RecordCountでも取得可能
try
with ADOQuery1 do begin
Close;
SQL.Clear;
SQL.Add('select CustNo, Company, Addr1, LastInvoiceDate from customer');
Open;
end;
except
exit;
end;
//レコード数
RecCnt := ADOQuery1.RecordCount;
//書込み範囲のセルをクリア
StartRow := 7;
StartRowStr := 'C' + IntToStr(StartRow);
EndRowStr := 'F' + IntToStr(StartRow + RecCnt - 1);
xlfClearRange(StartRowStr,EndRowStr);
LockWindowUpdate(PanelLeft.Handle);
Screen.Cursor := crHourGlass;
//開始時刻
TStart := TimeGetTime;
for i := 1 to 3 do begin
//ADORecordsetオブジェクトの内容をワークシートにコピー
xlfExcelSheet.Range[StartRowStr,StartRowStr].
CopyFromRecordset(ADOQuery1.Recordset, EmptyParam, EmptyParam);
end;
ADOQuery1.Close;
//終了時刻
TEnd := TimeGetTime;
Screen.Cursor := crDefault;
LockWindowUpdate(0);
//実行時間を表示
Memo3.Text := IntToStr(TEnd - TStart) + ' ms ';
//保存したことにする(つまり変更なし)
xlfBookSaved := True;
end;
end.
|