(Master/Details)主从表更新中的大问题(200分)

  • 主题发起人 主题发起人 maming
  • 开始时间 开始时间
M

maming

Unregistered / Unconfirmed
GUEST, unregistred user!
<<三层应用>>按delphi5中的主从表数据库应用的标准方法实现主从表数据的增删改。
但在从表增加多条数据的情况下数据就不对了。主表的主键与从表的主键都是正确的。
也就是想在应用服务器中对从表控制,却实现不了。
服务器端代码:
procedure Tremotedatamodule.Tdatasetprovede1BeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet;
DeltaDS: TClientDataSet;
UpdateKind: TUpdateKind;
var Applied: Boolean);
begin
if SourceDS.Name = 'qrymaseter' //主表
then
try
//处理主表的数据库
with deltadsdo
begin
edit;
FieldbyName('inputdate').Asstring := datetimetostr(now);
end;

except
on e:exceptiondo
...
end
else
try //从表
//处理从表的数据库
with deltadsdo
begin
edit;
FieldbyName('inputdate').Asstring := datetimetostr(now);
end;

except
on e:exceptiondo
...
end;
end;
客户端applyupdate数据后,主表的数据是对的,从表如果是多条数据也就是usinsert时,
只有一条成功,也不会出错,但只存入第一条数据,如果不对从表控制(删除第二个try)
的话,从表的多条数据可以成功的加入。请高手提个见意,如何解决这个bug(我想这是
个bug了)。
高分回报+万份感激...
 
急切的等待....
 
表中有自动增加的字段吗?
 
问题:
1. 为什么在Edit之后没有Post,加一下试试看。
2. 为什么没看见ukModify, ukDelete, ukInsert的区分,难道Delete时也要改inputdate吗
如果修改后还不行,就自己来写Insert语句,设置Applied为True,总应该可以的。
 
更正一下。
<<三层应用>>按delphi5中的主从表数据库应用的标准方法实现主从表数据的增删改。
但在从表增加多条数据的情况下数据就不对了。主表的主键与从表的主键都是正确的。
也就是想在应用服务器中对从表控制,却实现不了。服务器端代码:
procedure Tremotedatamodule.Tdatasetprovede1BeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet;
DeltaDS: TClientDataSet;
UpdateKind: TUpdateKind;
var Applied: Boolean);
begin
if SourceDS.Name = 'qrymaseter' //主表
then
try //处理主表的数据库
if UpdateKind=ukinsert
then
with deltadsdo
begin
edit;
FieldbyName('inputdate').Asstring := datetimetostr(now);
end;

except
on e:exceptiondo
...
end
else
try //处理从表的数据库
if UpdateKind=ukinsert
then
with deltadsdo
begin
edit;
FieldbyName('inputdate').Asstring := datetimetostr(now);
end;

except
on e:exceptiondo
...
end;
end;
客户端applyupdate数据后,主表的数据是对的,从表如果是多条数据也就是usinsert时,
只有一条成功,也不会出错,但只存入第一条数据,如果不对从表控制(删除第二个try)的话,
从表的多条数据可以成功的加入。请高手提个见意,如何解决这个bug(我想这是个bug了)。
 
改成这样也不行,那个deltaDS中的数据不能操作,也就是那个什么first,next没有作用,
而用recno可以移动但数据又不对了,移动recno之后就数据次序变了。
procedure Tremotedatamodule.Tdatasetprovede1BeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet;
DeltaDS: TClientDataSet;
UpdateKind: TUpdateKind;
var Applied: Boolean);
begin
if SourceDS.Name = 'qrymaseter' //主表
then
try //处理主表的数据库
if UpdateKind=ukinsert
then
with deltadsdo
begin
edit;
FieldbyName('inputdate').Asstring := datetimetostr(now);
end;

except
on e:exceptiondo
...
end
else
try //处理从表的数据库
if UpdateKind=ukinsert
then
with deltadsdo
begin
first;
while (Deltads.recno<=Deltads.RecordCount)do
begin
edit;
FieldbyName('inputdate').Asstring := datetimetostr(now);
Deltads.recno := Deltads.recno+1;
end;

end;

except
on e:exceptiondo
...
end;
 
高手快快帮帮忙
 
刚才测试了一下,确实有问题,但在source code里面又看不出问题,应该算是bug吧。
(1)运行次序
Provider.ApplyUpdates => Provider.InternalApplyUpdates => Provider.Resolver.ApplyUpdates
最后一个子程序中:
1.建立 FUpdateTree,此对象的Detail属性引用了自身,形成嵌套
MasterTable
|__Record1
| |__DetailTable
| |__Record1
| |__Record2 ...
|__Record2 ...
以上图为例,FUpdateTree的FDeltaDS代表主表的Delta,FSourceDS代表主表的源,
Detail[0..n-1]代表从属的n个DetailTable。
而Detail又是一个TUpdateTree对象
2.执行FUpdateTree.DoUpdate,这是一个对所有主从表Record的递归,流程如下:
从Delta的第一条记录到最后一条记录
判断是否Insert操作 ==> InternalUpdateRecord,Insert
递归调用所有子表Detail[0..n-1]
判断是否Modify操作 ==> InternalUpdateRecord,Modify
判断是否Delete操作 ==> InternalUpdateRecord,Delete
3.InternalUpdateRecord中
记住RecNo => 调用BeforeUpdateRecord => 回到记录号
(所以移动记录号是没用的,每个子表Delta记录都会循环到,不必自己建循环语句)
如果前面这个步骤没把Applied设为True,那么用DoUpdate、DoDelete、DoInsert执行数据操作
调用AfterUpdateRecord
(2)测试
在DataSetProvider的BeforeUpdateRecord和AfterUpdateRecord中设置消息显示。多条
新增的主表记录都正常,子表Delta只有第一条记录正常,其它都被略过。
如果去掉子表BeforeUpdateRecord的Insert中的Edit语句,一切正常。
说明第3步BeforeUpdateRecord中的Edit操作影响到了第2步中Delta从第一条到最后一条
的循环,我看不出是在哪一句跳出循环的。
(3)解决方法
如果在子表的BeforeUpdateRecord中不使用Edit,就不会有问题。如果一定要修改子表,
就自已生成一个Update查询,运行ExecSQL更新数据库,再设置Applied为True,跳过第3步的
DoInsert调用,也不会有问题。
// 以上代码不变
else
try //处理从表的数据库
if UpdateKind=ukinsert
then
with deltadsdo
begin
edit;
FieldbyName('inputdate').Asstring := datetimetostr(now);
post;
with UpdateSQLdo
begin
SQL.Clear;
SQL.Add('Insert into 子表 (Field0, Field0, ..) values (:v0, :v1, ..)');
Parameters.Items[0].Value := DeltaDS.FieldByName('Field0').Value;
Parameters.Items[1].Value := DeltaDS.FieldByName('Field1').Value;
......
ExecSQL;
end;
Applied := True;
end;

except
on e:exceptiondo
...
end;

 
DataSetProvider1中OPTIONS中有个属性poAllowMultiRecordUpdates设为TRUE试试。你可以
再看看帮助。
 
建议不要对deltads直接修改,再放多个tquery或tupdatesql来处理,方便不会错.
 
在三层中对于主细表的提交是很有问题的,
可以多放几个数据集,
有两个用于显示,
另两个用于提交数据,(用于提交的不必建立主细关系)
在提交完数据后,再从新刷新一下就是。
这可避免出现问题。
 
TO: Infernal Goddess
你分析很有理,你的做法也是成功的,
但不知还有没有好的办法,因为这个方法虽然可以实现,但是太麻烦了。
我试了以下办法,但是还是行不通,不知何因。
// 以上代码不变
else
try //处理从表的数据库
if UpdateKind=ukinsert
then
begin
mDeltaDS := Tclientdataset.create(self);
with mDeltaDSdo

begin
CloneCursor(DeltaDS, True);
edit;

FieldbyName('inputdate').Asstring := datetimetostr(now);
post;
end;
DeltaDS.CloneCursor(mDeltaDS, True);
Applied := True;
end;

except
on e:exceptiondo
...
end;

 
基本上解决了,
但方法有点笨,期待更好的办法。
到现在为止只有用下面这个办法了:增加一个mDetail:Datasetprovider和
一个mDeltads: Tclientdataset,设置mDetail对应从表的Dataset;
//以上同
else
try //处理从表的数据库
if UpdateKind=ukinsert
then
begin

mDeltads.providername :='mDetail';//从表对应的datasetprovider
mDeltads.open;
mDeltaDS.append;

for i:=0 to DeltaDS.fieldcount-1do
begin

mDeltads.fields.Value := DeltaDS.fields.Value;
end;
FieldbyName('inputdate').Asstring := datetimetostr(now);
mDeltads.applyupdate(-1);
mDeltads.close;
applied := True;
end;

except
on e:exceptiondo
...
end;
 
多人接受答案了。
 
后退
顶部