Delphi目前已經是國內常見的數據庫編程工具,它在各方面表現不錯,在支持大型多層數據庫結構的同時也完全支持本地數據庫。對於本地數據庫中Delphi對FoxPro的支持也不錯,我曾經對VB、FoxPro、Delphi的數據庫操作速度進行比較,發現除了啟動速度較慢外,其它各項Delphi均排在首位。而且Delphi幾乎支持所有原先FoxPro所擁有的功能,對於有一定編程經驗的人來說,Delphi成了編寫數據庫軟件的一把利器,因而Delphi迅速流行起來,Delphi在我國也占有一席之地。
但是我在使用Delphi編程的過程中發現它也有不如人意的地方,特別是在本地數據庫DBase和Paradox方面,它居然不支持十分有用的Pack和Undelete功能。對於數據庫來說,因為是一個順序存儲文件,在刪除部分記錄時一般采用了軟刪除技術,也就是說將要刪除的記錄標記已刪除的標記,但並不立即從物理上刪除這些記錄(刪除後庫文件大小不變)。
這樣做可以避免僅僅刪除一條記錄就要將整個數據庫重新寫入存儲器,提高了讀寫的效率。但是如果數據庫長期不對已經被標記為刪除的記錄進行整理和真實刪除,數據庫就會越來越大,其中無用的數據所占的比率越來越大,使得數據庫的讀寫效率迅速下降,而且造成查詢速度的減慢。特別是在編寫人事管理數據庫應用軟件時,庫內要保存大量的圖形文件(人員的照片),而這些數據還時常需要添加和刪除,沒用的數據不能"Pack"將占用巨大的硬盤空間,而且還將降低系統的運行速度。
這是編寫數據庫軟件的人必須考慮的一個問題。經過再三查找,我找到一些資料,解決了如何對數據庫進行pack,下面是其中的核心程序的一個例子,在看例子之前,我先做一些說明:
1. 對於DBase和Paradox庫,其Pack原理是不同的:如果是Paradox 表, 必須調用 DbiDoRestructure,重建數據庫結構;如果是 DBase 表, 只需要調用DbiPackTable。
2. 在做Pack刪除時,必須以獨占方式打開數據庫時。具體請參考一下的例子。
(為了節省篇幅,例子中只給出關鍵性的程序段)
implementation
uses BDE;//做PACK必須引用次單元裡的函數
{$R *.DFM}
procedure TfrmPack.FormCreate(Sender: TObject);
var
DBName:String;
begin
DBName:=ExtractFilePath(Application.ExeName);//得到數據庫的位置
{設置Table}
tblDBase.DatabaseName:=DBName;
tblParadox.DatabaseName:=DBName;
tblDBase.TableName:='dbsTest.dbf';
tblParadox.TableName:='pdxTest.db';
tblDBase.Active:=True;
tblParadox.Active:=True;
end;
{物理刪除數據庫記錄Pack a Paradox or DBase table}
// The table must be opened execlusively before calling this procedure...
procedure TFrmPack.PackTable(FTable: TTable);
var
Props: CURProps;
hDb: hDBIDb;
TableDesc: CRTblDesc;
begin
FTable.Active := False;
{當數據庫打開失敗時,這個循環語句能夠讓用戶重試}
repeat
try
FTable.Exclusive := True;
FTable.Active := True;
{如果正常打開數據庫,則退出循環}
Break;
except
on EDatabaseError do
if Application.MessageBox(
'以獨占方式打開數據庫時,出現錯誤---重試否?',
'數據庫錯誤',
MB_OKCANCEL + MB_DEFBUTTON1) < > IDOK then
Exit;
end;
until False;
try
{Check()用於校正和報告DBI底層錯誤;DbiGetCursorProps()用於取表光標屬性}
Check(DbiGetCursorProps(FTable.Handle, Props));// 獲得表的屬性已得到表的類型
{如果是Paradox 表, 必須調用 DbiDoRestructure,重建數據庫結構}
if (Props.szTableType = szPARADOX) then
begin
FillChar(TableDesc, sizeof(TableDesc), 0);
{從數據表光標獲取數據庫句柄}
Check(DbiGetObjFromObj(hDBIObj(FTable.Handle), objDATABASE, hDBIObj(hDb)));
{設置表的描述結構的Name/Type/bPack屬性}
StrPCopy(TableDesc.szTblName, FTable.TableName);
StrPCopy(TableDesc.szTblType, Props.szTableType);
TableDesc.bPack := True;
{關閉表並調用api}
FTable.Close;
Application.ProcessMessages;
Check(DbiDoRestructure(hDb, 1, @TableDesc, nil, nil, nil, False));
Application.ProcessMessages;
FTable.Open;
end
{ 如果是 DBase 表, 只需要調用DbiPackTable...}
else
if (Props.szTableType = szDBase) then
begin
Application.ProcessMessages;
Check(DbiPackTable(FTable.DBHandle, FTable.Handle, nil, szDBase, True));
Application.ProcessMessages;
end
{不是DBase和Paradox表}
else
raise EDatabaseError.Create('數據庫必須是 Paradox 或者 DBase 類型,才能進行物理刪除操作!!');
finally
FTable.Active := False;
FTable.Exclusive := False;
FTable.Active := True;
end;
end;
procedure TfrmPack.BitBtnDBaseClick(Sender: TObject);
begin
if OpenPictureDlg.Execute then
DBImage1.Picture.LoadFromFile(OpenPictureDlg.FileName);
end;
procedure TfrmPack.BitBtnParadoxClick(Sender: TObject);
begin
if OpenPictureDlg.Execute then
DBImage2.Picture.LoadFromFile(OpenPictureDlg.FileName);
end;
procedure TfrmPack.BitBtnPackDBaseClick(Sender: TObject);
begin
PackTable(tblDBase);//物理刪除dDBSE庫
end;
procedure TfrmPack.BitBtnPackParadoxClick(Sender: TObject);
begin
PackTable(tblParadox);//物理刪除Paradox庫
end;
本程序在Windows 2000 Professional下Delphi5.0 C/S版調試通過。調試時可裝入較大的Bmp圖片,先點"?"刪除記錄,然後查看數據庫的大小;再點"物理刪除"按鈕,再查看數據庫的大小,你會發現數據庫變小。本例子還可以進一步擴展,以增強其功能,限於篇幅,不必在此討論。有興趣的朋友可給我發Email:
[email protected],我可進一步解答有關的問題。