关于PARADOX数据库的问题(200分)

G

goujie

Unregistered / Unconfirmed
GUEST, unregistred user!
在我的系统中用了PARADOX的数据库,数据库文件越来越大,
删除了纪录数据库文件大小仍然不变,是不是要进行数据压缩
来删除逻辑逻辑删除的数据,如何实现呢 ?
 
可以对数据表进行释放啊
pack table 可以释放很多空间

 
to lanbing1400:
能详细说明一下吗?谢谢
 
转贴:
为Delphi提供Pack和Undelete功能

本文针对Delphi3和Delphi4,对于C++Builder同样也适用,在数据库引擎BDE4.0、4.5、5.0中经过测试。

Delphi目前已经是国内常见的数据库编程工具,它在各方面表现不错,在支持大型多层数据库结构的同时也完全支持本地数据库的支持。对于本地数据库中Delphi对FoxPro2.5b的支持也不错,我曾经对VB、FoxPro2.5b、Delphi的数据库操作速度进行比较,发现除了启动速度较慢外,其它各项Delphi均排在首位。而且Delphi几乎支持所有原先FoxPro所拥有的功能,对于有一定编程经验的人来说,Delphi成了编写数据库软件的一把利器,因而Delphi迅速流行起来,我几年前甚至利用Delphi 1.0和汇编配合制作了工业控制系统的实时前后台软件。

但是在我长期使用Delphi编程的过程中发现它也有不如人意的地方,特别是在本地数据库FoxPro方面,它居然不支持十分有用的Pack和Undelete功能。对于数据库来说,因为是一个顺序存储文件,在删除部分记录是一般采用了软删除技术,也就是说将要删除的记录标记已删除的标记,但并不立即从物理上删除这些记录。这样做可以避免仅仅删除一条记录就要将整个数据库重新写入存储器,提高了读写的效率。但是如果数据库长期不对已经被标记为删除的记录进行整理和真实删除,数据库就会越来越大,其中无用的数据所占的比率越来越大,使得数据库的读写效率迅速下降,而且造成查询速度的减慢。在编写FoxPro数据库时有个规律,如果数据进出数据库频繁,也就是说不断删除旧的数据,加入新的数据的情况下每次在关闭数据库的时候或打开数据库的时候先做一次Pack,对数据库进行整理,将软删除的记录真正从数据库中删除。但是在Delphi的各个版本中数据库控件均未提供Pack的功能,这使得用Delphi编写的数据库软件如果使用本地数据库的形式,数据库的大小增长很快,即使删除了大量记录,数据库的大小没有任何改变。以前我的做法是在使用这类数据库软件一段时间后利用FoxPro打开数据库进行Pack,但是这样做要求客户端拥有FoxPro,而且经常这么做也很繁。于是我开始查找有关资料,为什么Delphi无法使用pack功能?

经过再三查找,我找到一些资料,说明如何对数据库进行pack了,在将其改编后在Delphi中使用通过。下面是其中的核心程序。

BDE API Call:

DBIResult DBIFN DbiPackTable (hDb, hCursor, pszTableName, [

pszDriverType], bRegenIdxs);

在数据库引擎的底层函数调用中,我找到了这个函数,这个函数据说可以对FoxPro的数据库进行Pack,其中hDb是数据库的句柄,可以由Table.Handle获得;hCursor是数据表(Table)关联的游标,如果为NULL则数据表依赖于pszTableName和pszDriverType这两个传输决定数据库的来源;bRegenIdxs决定是否关联外接的索引文件尤其是MDX多索引文件。

具体的操作函数如下:

procedure PackTable(Table: TTable);

var

Props: CURProps;

hDb: hDBIDb;

begin

if not Table.Active then

raise EDatabaseError.Create('Table必需已经打开');

if not Table.Exclusive then

raise EDatabaseError.Create('Table必需以独占方式打开');

Check(DbiGetCursorProps(Table.Handle, Props));

if (Props.szTableType = szDBASE) then

Check(DbiPackTable(Table.DBHandle, Table.Handle, nil, szDBASE, True))

else

raise EDatabaseError.Create('Table必需是dBASE或FoxPro类型');

Table.Open;

end;

特别对这段源码说明一下,这段源码部分改编自C,已经在上面提到过的环境中测试通过,需要注意的是,数据表必需以独占方式打开,简单的说,就是在设计时将数据表关闭(属性Active=False),在运行时才打开数据表(form_onCreate时Table.Open),这样才能保证数据表在独占方式下被打开。DbiGetCursorProps()是一个读取数据表的属性的底层函数调用,返回了大部分通用的数据表属性。Check()函数可以简单的处理数据库的出错提示和异常处理,如果出现函数调用错误会自动显示出错信息。

说了pack那么与之对应的Undelete也就更要解释了,同样我找到了将软删除的数据恢复的函数,不过这个函数隐藏的更深,连C代码都没有,所以以下的代码完全由我自己摸索出来,仅在上面说到的环境中测试通过。

首先介绍一下相应的底层函数调用:

DBIResult DBIFN DbiUndeleteRecord (hCursor);

这个函数仅有的参数就是关联的数据表的游标,您可以在BDE.int的文件中找到它的声明,但是具体的使用说明含糊不清,到底是恢复当前被删记录还是将数据表内所有被软删除的记录全部恢复?由于缺少相应的代码分析,我参照FoxPro的经验进行了多次实验,在十几种方案的对比下得出了结论,这条函数调用仅仅恢复当前的被软删除的记录。因此必需首先将数据表的游标移动到被删除的记录上,然后调用这条函数,才会有所反应。要知道在默认的情况下,数据库控件是不会将游标移动到被删除的记录上的,所以必需首先将数据表的读写属性修改,打开软删除的属性,使得遍历数据表时可以访问到被标记为删除的记录。下面是一段实现将数据表中所有被软删除的记录恢复的源代码。其它形式的反删除可以参照这段代码。

procedure Ttablepro.undelete;

var

CProps: CurProps;

rslt: DBIResult;

bm:TBookmark;

rp:pRECProps;

begin

Check(DbiGetCursorProps(self.Handle, CProps));//取得数据表的属性

if (StrIComp(CProps.szTableType, szDBASE) <> 0) then//如果不是Dbase或Foxpro则退出

raise EDBEngineError.Create(DBIERR_NOTSUPPORTED);

rslt:=DbiValidateProp(hDBIObj(self.Handle), curSOFTDELETEON, True);

//可否设置软删除?

if (rslt = DBIERR_NONE) then

Check(DbiSetProp(hDBIObj(self.Handle), curSOFTDELETEON, Longint(true)));

//设置为可以软删除

Check(DbiGetCursorProps(self.Handle, CProps));

//更新数据表的属性

if (CProps.bDeletedOn = False) then

raise EDatabaseError.Create('软删除没有设置!');

//取得当前的记录位置

bm:=self.GetBookmark;

//将游标移动到第一个记录以前!Not Table.First!

Check(DbiSetTobegin(self.handle));

//不断移动,直到到数据表的最后记录

while (DBIGETNEXTRECORD(self.handle,dbinolock,nil,nil)=DBIERR_NONE) do

begin

//请关闭Delphi的异常响应,以便执行下面的语句!

try

check(DbiUndeleteRecord(self.Handle));

except

//Do somthing here !

end;

end;

//取回原先记录的位置,重新定位

self.GotoBookmark(bm);

self.FreeBookmark(bm);

end;
 
呵呵,从离线数据库里COPY的,算不算偷的。

助人一臂,偷又何妨。呵呵
 
不通过程序的话也可以进入表的设计模式,选中PACK TABLE,保存。OK!
 
to wwolf:
你的方法好像对PARADOX不行,我试了,不过谢谢你,一样给分。
ro lanbing1400:
你的方法可以但是有没有方法用代码实现?
 
现在手头没有,给我个信箱,有机会我找到后发给你
 
换个思路,换种数据库吧
 
goujies@sohu.com谢谢,一会给分。
 
不行,因为已经有用户在使用了。所以换数据库不是一件很容易的事!
 
再COPY一个,呵呵。。。

Example 1: Pack a Paradox or dBASE table.
This example will pack a Paradox or dBASE table therfore removing already deleted rows in a table. This function will also regenerate all out-of-date indexes (maintained indexes). This example uses the following input:

PackTable(Table1)

The function is defined as follows:

// Pack a Paradox or dBASE table
// The table must be opened execlusively before calling this function...
procedure PackTable(Table: TTable);
var
Props: CURProps;
hDb: hDBIDb;
TableDesc: CRTblDesc;
begin
// Make sure the table is open exclusively so we can get the db handle...
if not Table.Active then
raise EDatabaseError.Create('Table must be opened to pack');
if not Table.Exclusive then

raise EDatabaseError.Create('Table must be opened exclusively to pack');

// Get the table properties to determine table type...
Check(DbiGetCursorProps(Table.Handle, Props));

// If the table is a Paradox table, you must call DbiDoRestructure...
if Props.szTableType = szPARADOX then begin
// Blank out the structure...
FillChar(TableDesc, sizeof(TableDesc), 0);
// Get the database handle from the table's cursor handle...

Check(DbiGetObjFromObj(hDBIObj(Table.Handle), objDATABASE, hDBIObj(hDb)));
// Put the table name in the table descriptor...
StrPCopy(TableDesc.szTblName, Table.TableName);
// Put the table type in the table descriptor...
StrPCopy(TableDesc.szTblType, Props.szTableType);
// Set the Pack option in the table descriptor to TRUE...
TableDesc.bPack := True;
// Close the table so the restructure can complete...
Table.Close;
// Call DbiDoRestructure...

Check(DbiDoRestructure(hDb, 1, @TableDesc,
nil, nil, nil, False));
end
else
// If the table is a dBASE table, simply call DbiPackTable...
if (Props.szTableType = szDBASE) then
Check(DbiPackTable(Table.DBHandle, Table.Handle, nil, szDBASE, True))
else
// Pack only works on PAradox or dBASE; nothing else...
raise EDatabaseError.Create('Table must be either of Paradox or dBASE ' +

'type to pack');

Table.Open;

end;
 
谢谢,可以了^_^,给分
 
顶部