类似ClientDataSet.FetchDetails、ClientDataSet.FetchBlobs的问题,有点难 (100分)

  • 主题发起人 主题发起人 hyhy
  • 开始时间 开始时间
H

hyhy

Unregistered / Unconfirmed
GUEST, unregistred user!
1.主子表程序,在设计程序时,由于有要求,数据取完之后立即断开连接,类似WebService,所以使用此方法取数据:
GetData(const SQLstr:string;out vdata:Olevariant);
其中:SQLstr为查询语法,
vData为返回数据。
中间层用ClientDataSet+DataSetProvider+SQLquery实现,由于要保证速度,在中间层的DataSetProvider中把[Options]poFetchDetailsOnDemand、poFetchBlobsOnDemand等相关的属性都置成了True,所以在开始时子表数据和Blob数据都没有取,在客户端需要例如滚动时才去取,这时候由于不是直接Midas连接,已经不能直接调用ClientDataSet.FetchDetails、ClientDataSet.FetchBlobs等方法了,请问如何用其他的方法实现ClientDataSet.FetchDetails、ClientDataSet.FetchBlobs一样的功能?
2.在这种情况下,更新数据我又采用了
UpdateData(Const Delta:OleVariant;out errData:OleVariant);
其中:Delta为ClientDataSet的Delta,
errData为返回的没有更新成功的记录。
这个方法如何实现?
errData返回后如何将客户端的Delta更换,以节省下次修改更新提交的数据量?
该问题累计共400分,另外300分在
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2566290
这两个问题可能困扰了很多程序员,如果能够解决相信会对大家都有很大的帮助,希望大家踊跃回答,能答一个算一个。
 
1。取主表的Key,根据Key用同一方法取相关Detail表的数据。
2.一次取所有的从表数据到客户端,然后用Filter过滤.
================================
上面两个方法你随便选一个应该可以解决问题的.
 
to yayongm:
1.取了之后如何加到客户端的子表ClientDataSet中,而不让客户端认为是新增数据。
2.就是为了加速才在开始不取Detail的,不然就不上这里提问了。
 
ClientDataSet.Data := vdata ;
对于detail我建议还是先取出,否则通过主表的MASTERID去取明细表的记录,这样会有许多的网络往还,速度会更慢。
 
在吗?能不能请你帮我去看看我的问题?就是需要雷锋的那个。谢谢!
 
to dxajeje:
由于表涉及到三层或更多主从,即主+从+从,实在是慢,如果是两层还没啥太大问题,三层主从在后台由于Vcl的实现原理原因造成点触发太频繁所以才慢,所以才有先取主表的想法。
主表的MASTERID去取明细表的记录的方法我试过,就是不知道如何把取来的数据加到Detail表中又不让ClientDataSet认为是新增数据,我用Move方法复制也没效果。
ClientDataSet.FetchDetails的实现方法很绕人,不好读,所以我想要一个有人已经实现了的方法。
 
在三层设计中,明细表将做为主表的一个字段打包传到客户端,这样的话,将字段的值改变了,ClientDataSet该如何处理呢?
如果每条主记录都要重取明细的话,用ClientDataSet.FetchDetails方法我想速度要慢。(特别是通过INTERNET),我现在正在实施一项目。

 
如果可以,将你的相关代码发到我的邮箱,我看看(dxajeje1979@163.com)
 
to dxajeje:
我试过,但客户要求快速显示数据,因为子表我放在另一页面,数据滚动时并不取子表数据,所以不是每条主记录都要重取明细,只是用户想看子表数据切换页面时才去取主表当前记录的子表数据,用的是障眼法,客户不会觉得慢,倒是一次性去取完再显示,用户觉得慢的忍无可忍,一方面也是用户较多造成的,大概有70个客户端吧,主表的记录倒不是太多,子表反而成千上万条,用户仅从主表记录数量无法理解为何慢,我用的就是WebService。
Midas中用ClientDataSet.FetchDetails方法并没有改变表的增删改属性,我要的也是这效果,源码不太好读,所以到这里讨方法来了。
 
to dxajeje:
没有什么源码,就是在中间层的DataSetProvider中把[Options]poFetchDetailsOnDemand属性设成True,ClientDataSet的FectchOnDemand设成False,取出的记录就没有子表数据了。
客户端用了以下方法,但不行。
procedure TForm1.Button18Click(Sender: TObject);
var
SqlStr:String;
vData:oleVariant;
b1,b3:Pchar;
L:integer;
begin
//ClientDataSet1 主表
//ClientDataSet2 子表,和主表关联但没数据
//ClientDataSet3 只有一条记录但带有相关子表记录的数据集
//目的:用ClientDataSet3中的包含的子表字段所代表的记录集替换ClientDataSet1当前记录的子表数据集字段,即在ClientDataSet2中显示数据,但不改变ClientDataSet1的当前Delta属性。
if ClientDataSet2.RecordCount<=0 then

begin
try
SQLstr:='select * from test_table10 where AAA='''+ClientDataSet1.FieldByname('AAA').asstring+'''';
SocketConnection3.connected:=True;
SocketConnection3.AppServer.GetDetailData(SQLstr,vData);
ClientDataSet3.Data:=vData;//返回的结果就是一条带子表记录的单记录
L:= ClientDataSet3.RecordSize;
getmem(B1,L);
getmem(B3,L);
ClientDataSet3.GetCurrentRecord(B3);
ClientDataSet1.GetCurrentRecord(B1);
move(B3^,B1^,L);
finally
SocketConnection3.connected:=False;
//freemem(B1, L);
freemem(B3, L);
end;
end;
end;
 
其实这个问题很简单,解决的前提是明白MIDAS取数据和更新数据的流程。要点如下:
1、不要在AppSvr层上使用ClientDataSet的主从关联让MIDAS来自动取数据。而是使用主表的KEY来一次取一定数量的主表和从表数据。
2、按照一定的业务逻辑和主从表的层数限制客户端主表取到的数据量。
3、更新数据时要使用事务更新主从表的数据。
对于你的问题解决的方法如下:
1、在AppSvr上先取主表数据送到客户端,当用户需要查看子表数据时再根据主表记录的KEY来Call AppSvr上的方法取从表的对应数据送到客户端。关键的代码如下:
//客户跳转查看明细数据时
if not 子表.Active then
begin
CallAppSvrEvent(MasterTableKey, vData);
子表.AppendData(vData)
end
else
not if 子表.Locate(MasterTableKey) then
begin
CallAppSvrEvent(MasterTableKey, vData);
子表.AppendData(vData)
end;

子表 .Filter(MasterTableKey);

注意:这里的vData是AppSvr端送过来的ClientDataSet.Data。
算了,懒得写了。因为我使用别人的电脑回答问题,明天晚上我粘一些代码上来。
 
我是csvmm,我可能没说清楚,不是那回事。我在记录窗体中只是创建了查询窗体后退出,不要查询结果,回到记录窗体后,原来记录窗体中使用正常的一个方法就不能正常使用了,那个方法和查询窗体无关,不需要使用查询窗体返回的结果集。 你能再看看吗?谢谢!
 
问题1的解决概要代码:
if not 子表.Active then
begin
CallAppSvrEvent(MasterTableKey, vData);
子表.Data := vData;
end
else
not if 子表.Locate(MasterTableKey) then
begin
CallAppSvrEvent(MasterTableKey, vData);
子表.AppendData(vData)
end;

子表 .Filter(MasterTableKey);
注意:vData是AppSvr上的一个方法取得的ClientDataSet.Data,也就是先在AppSvr上得到客户端需要的Data再送到客户端。在RemoteDataModule上放ClientDataSet->ProviderDataSet->SQLDataSet. Open ClientDataSet就可以得到这个Data。示例代码如下:
qryTemp.SQL.Text := 'Select * from DetailTable where MasterID = asterTableKey';
cdsTemp.Open;
vData := cdsTemp.Data;
cdsTemp.Close;
 
问题2:
客户端
CallSvr(MasterClientDataSet.Delta, DetailClientDataSet.Delta, Return);
//如果更新成功
if Return = OK then
begin
MasterClientDataSet.MergeChangeLog;
DetailClientDataSet.MergeChangeLog;
服务器端:
ADOConnection.begin
Trans;
try
ADOConnection.CommitTrans;
更新主表
更新从表
Return = OK;
except
Return = ERR;
ADOConnection.RollbackTrans;
end;

关于:errData返回后如何将客户端的Delta更换以节省下次修改更新提交的数据量?
Borland不允许你修改Delta,你就死了这条心吧。如果想,可以考虑在适当的量上Post数据到服务器。
 
to blue_morning:
谢谢你的参与,第一个方法你提供的可行性很好。如果没有更好的方法我就准备采用。
不过我确实很想利用一下Delphi的内部机制来解决,倒不是我要钻牛角尖,也是想要提高一下自身水平,只是ClientDataSet.FetchDetails的实现源码很绕人,不好读,又使用了游标管理,但它的思路还是有一定借鉴意义,阅读难点主要在游标取回数据后的替换,可惜没有资料介绍。
第二个方法我自己解决用的方法和你一样。至于Delta,表面上好像是只读的,但从DataSetProvider的OnUpdateError事件描述出错处理的帮助上来看,好像还有商榷余地,我的errData返回的记录都是从这里取的。只是我水平不行,可能属于难度很高的问题,如果实在不能解决,我也不打算深究,毕竟还有容错回滚,大不了数据改完后一块提交。
最后还是谢谢你的参与,我们共同进步,同时希望你能到
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2566290
登记一下,问题一模一样,只是到时候我好给分,这里分太少了。
 
关于问题2,你的更新错误本质的问题是并发造成的,如何解决并发的操作才是问题的根本所在,不是吗?
至以Delta的处理,将Delta赋值给一个ClientDataSet.Data就可以处理了。处理完后又把ClientDataSet.Data做为Delta去更新数据就可以了。这只是个思路,我没有试过。
已去登记了。
 
后退
顶部