文件为转载---http://www./space-14160-do-blog-id-514.html
嵌套ClientDataset:
1: 当ClientDataset.packetRecords=-1时,不论客户端的ClientDataset.fetchOnDemand和服务端的DatasetProvider.FetchDetailOnDemand如何设置均会导致速度下降到难以忍受。
1.1原因解析:因为服务端datasetProvider指向的主表Dataset会在刚打开时就为每一条纪录整理其Detail
数据(循环发送Sql语句到数据库,并cache到子表的dataset,供以后的Filter使用)。所以服务端的Dataset打开会非常慢。
1.2:解决方案:只有返回一条以编辑或增加一条主表纪录时才使用嵌套ClientDataset。其他浏览时应动态刷新Detail控件。
1.3或者设定ClientDataset.packetRecords为一个比较小的值(100以内)。
1.3 clientdataset的fetchonDemand属性。基本不影响速度(它不影响无服务端封装Detail的行为)。它只设定clientdataset.next时是否自动GetNextPacket.
1.4 DatasetProvider.FetchDetailOnDemand;使用Table作为子表会影响一倍左右的速度(1条主表纪录对应平均4条子表纪录时。使用query作子表时速度基本一样。)
1.5 不论ClientDataset.packetRecords如何设置,服务端的主表qry都从数据库中一次取出全部纪录。
1.6 慎用ClientDataset.IndexName,它会导致读入所有纪录到客户端。
2、结论:对于存在主子表显示或编辑要求的客户端需要使用PacketRecords属性设为较小值。(此类客户端一般不会要求显示太多数据)。可以将服务端的DatasetProvider.FetchDetailOnDemand设置为false影响不大,还可简化客户端代码。对于统计查询的数据不要使用主子表关系。Tips:在子表的Sql中可以返回连接表数据。并设置相关字段为not pfInUpdate,可以简化客户端代码(不用使用计算字段)并能够编辑更新子表数据。
3.所有需要更新的Clientdataset均不能与其他的Clientdataset共享一个DatasetProvider.
4、设置PackageRecordcount<>-1的Clientdataset不能与其他的Clientdataset共享一个DatasetProvider.
5、对于浮点数可能会导致更新ClientDataset出现"Record Changed by Another user"错误,这是由于float截断导致的。将对应的DatasetProvider的Updatemode=whereKeyOnly.将联系的
query字段PKNO的providerFlag设置为pfInkey.
6、怎样更新一个多表连接的结果集?i.设置datasetProvider.onGetTableName事件,设定要更新的表。ii.将相关的query字段(来自其它表的字段)的provideFlag去除pfInputDate属性。
7、将编辑修改用的clientdataset和查询浏览用的clientdataset的provider分开的原因:使客户端的编辑窗口比较独立于其他窗口,降低耦合度,提高编辑浏览详细窗口的通用性。
8、不将编辑修改用的clientdataset和查询浏览用的clientdataset的provider分开的原因:修改后不用重新刷新查询的clientdataset。不用重新定义计算字段、
by chenglh
2003-07-10
2003-11-25 14:09:00
发表评语»»»
2005-8-22 20:21:37 关于主从嵌套TclientDataset补充比较合理的主从表方案:不使用嵌套,但将全部符合条件的主表和从表读入客户端。
然后再客户端处理改变主表位置时的界面显示。这样速度最快。与服务段通讯(round trip)最少。
例子:
2003年的工资单表和明细表。
cds工资单表.sql:=select * from 工资单表 where year =2003
cds工资明细表.sql:=select * from 工资单明细表 where 工资单表ID in (select ID from 工资单表. where year =2003)
客户端的界面自己写代码处理动态显示相应的工资明细情况。
对于修改或增加工资记录。可以写一个比较通用的函数来处理外键的引用一致。(这个函数可以很简单地实现)
2005-9-20 10:15:04 利用TClientDataset作通用的数据库访问如上所述,基本可以不用TClientDataset的嵌套功能。
当不使用嵌套功能时,可以利用TClientDataset的Data和Delta作一个通用的数据库读取和更新的数据模块。(实际上,在大多数情况下,客户端的TClientDataset是可以共用一个TDatasetProvider的)。这样客户端全部可以使用TClientDataset,程序从2层转到3层(或者反过来)时,修改的代码都在十几行内(不论项目有多大)。后面将给出这个实现方法的代码。
Tips:客户端的TClientDaset常常需要设置永久字段,用来更改Field的DisplayLabel,ProviderFlags等,另外,需要加入计算字段时,其他的非计算字段也必须设置为永久字段。 如何给客户端的TClientDataset方便地加入永久字段? 方法是:用普通的方法连接好数据库,设置sql,然后用右键菜单"Add Fields",把字段加进去。再进行设置。
Tips:怎样更新来自多表连接的sql取来的数据集cdsJoin? 答:步骤:1、设置cdsJoin的永久字段,2、给不用更新的来自不用更新表中的字段,设置其ProviderFlags.pfInWhere,pfInUpdate均为False 3、用本笔记后面代码中的SaveCdsParital函数保存即可。
下面是一个通用的数据访问代码。(注:本来为该代码定义的一个接口,为了简化,将接口去掉了。)
unit untDmDBDealer;
{
<remark>
声明:您可以修改和分发本单元的代码。
如果您在项目中使用本代码,请保留该pas文件的文件头部说明。
功能:为untDBRoutine单元提供一个IDBDealer接口的实现。
编制人:程龙华
时间:2005-09-12
注意:本单元为公共单元,不含与任何特定项目有关的业务逻辑。
常用的(标准)使用方法:
1.创建对象aInstance:=TdmDBDealer.create(aOwner);
2.设定对象的连接ADO字符串aInstance.ConnectionStr:=aStr;
3.将untDbRoutine.DBDealer指向该对象。untDbRoutine.DBDealer:=aInstance as IDbDealer;
4.现在才可以正常使用untDbRoutine单元的各个函数。
begin
untDBRoutine.OpenCDS('select * from Table where ...',ClientDataset1);
...
untDBRoutine.ExecuteSQL('update table ...');
end;
</remark>
}
interface
uses
SysUtils,Forms, Classes, ADODB, Provider, DB, Variants, dxDBCtrl, dxDBTL, dxDBTLCl,
dxEdLib, dxDBELib, Controls, dxTL, DBClient, math, Windows,untDBRoutine,untCommon;
type
//数据模块,实现IDBDealer接口.
TdmDBDealer = class(TDataModule{,IDbDealer})
ADOConnection: TADOConnection;
ADOCmd: TADOCommand;
dsOpenData: TADODataSet;
dspOpenData: TDataSetProvider;
cdsTemp: TClientDataSet;
ADOQuery1: TADOQuery;
dsPartialSaveData: TADODataSet;
dspPartialSaveData: TDataSetProvider;
dsSaveData: TADODataSet;
dspSaveData: TDataSetProvider;
procedure dspSaveDataBeforeApplyUpdates(Sender: TObject;
var OwnerData: OleVariant);
procedure DataModuleCreate(Sender: TObject);
procedure dspSaveDataUpdateError(Sender: TObject;
DataSet: TCustomClientDataSet; E: EUpdateError;
UpdateKind: TUpdateKind; var Response: TResolverResponse);
procedure dspParitalSaveUpdateData(Sender: TObject;
DataSet: TCustomClientDataSet);
procedure dspPartialSaveDataGetTableName(Sender: TObject; DataSet: TDataSet;
var TableName: String);
procedure DataModuleDestroy(Sender: TObject);
procedure ADOConnectionWillConnect(Connection: TADOConnection;
var ConnectionString, UserID, Password: WideString;
var ConnectOptions: TConnectOption; var EventStatus: TEventStatus);
procedure dspPartialSaveDataBeforeApplyUpdates(Sender: TObject;