关于三层主从表数据添加,删除,更新的方案实现【高手关注200分】(200)

  • 主题发起人 主题发起人 shine_yzl
  • 开始时间 开始时间
S

shine_yzl

Unregistered / Unconfirmed
GUEST, unregistred user!
通过网络狂搜发发现解决方案有两点好的建议1,不要用任何有自增字段,或者默认值得表结构;2,不要使用主从表结构。总结最好的方法在客户端把变化了的clientdataset1.delta送去服务端! 执行服务端的方法!即socketconnection1.appserver.fangfa(clientdataset1.delta);
然后在服务端分析clientdataset1.delta,分解成相应的sql执行!成功返回true,错就false 放弃使用applyupdate方法!它太多问题了!但是现在,这个方法怎么实现,由于我用的是ACCESS数据库,可否贴例子
 
不用主从表这样就可以了在主表记录滚动时,根据主从结构的字段,在子表中过滤在新增子表记录时,将主表主从结构的字段带入子表删除主表记录时,同时删除子表中相关记录修改主表记录的时候当然也要注意主从结构的字段是否修改,在读取数据的时候,也要根据主表的主从结构字段在子表中filter保存的时候,就用中间层一次保存两个表的接口来保存.这个时候是不用管它是否是主从结构
 
1.生成SQL,我是参考了别人的哈function TRDM.gensqls(const sqlconn:widestring;pdelta: OleVariant;
const ptablename,pkeyfields: WideString):widestring;vari,j:integer;s1,s2,s3:String;Cmdstr:string;FieldList,Keylist:TstringList;Cdsupdate:Tclientdataset;sqlstr:widestring;
begin
if VarisNull(pdelta) then
Exit;
try FieldList:=Tstringlist.Create;
Keylist:= Tstringlist.Create;
Keylist.Delimiter:=',';
Keylist.DelimitedText:=pkeyfields;
DM.TMP.SQLConnection:=getsqlconn(sqlconn);
DM.TMP.Close;
DM.TMP.SQL.Text:='select * from '+ ptablename+' where 1=0';
DM.TMP.GetFieldNames(FieldList);
Cdsupdate:=Tclientdataset.Create(nil);
Cdsupdate.Data:=pdelta;
if not Cdsupdate.Active then
Cdsupdate.Open;
for i:=1 to fieldList.Countdo
if Cdsupdate.FindField(FieldList[i-1])<>nil then
cdsupdate.FindField(FieldList[i-1]).tag:=1;
FieldList.Free;
if CDSupdate.RecordCount>0 then
begin
CDSupdate.First;
s1:='';
s2:='';
while not CDSupdate.Eofdo
begin
CmdStr:='';
case Cdsupdate.UpdateStatus of usUnmodified://从原数据行取得修改条件 begin
s2:='';
for j:=1 to Keylist.Countdo
begin
if s2='' then
s2:=Keylist[j-1]+'='+Vartosql(Cdsupdate[Keylist[j-1]]) else
s2:=s2+' and '+Keylist[j-1]+'='+Vartosql(Cdsupdate[Keylist[j-1]]);
end;
end;
usModified: begin
s1:='';
for i:=1 to CDSupdate.FieldCountdo
begin
if (not cdsupdate.Fields[i-1].isNull) and (Cdsupdate.Fields[i-1].tag=1) then
begin
if s1='' then
s1:=Trim(CDSupdate.Fields[i-1].FieldName)+' = '+vartosql(Cdsupdate.Fields[i-1].value) else
s1:=s1+','+Trim(CDSupdate.Fields[i-1].FieldName)+' = '+vartosql(Cdsupdate.Fields[i-1].value);
end;
end;
if s1<>'' then
begin
CmdStr:=' update '+ptablename+' set '+s1+' where '+s2;
end;
end;
usInserted: begin
s1:='';
s2:='';
for i:=1 to Cdsupdate.FieldCountdo
if (not Cdsupdate.Fields[i-1].isnull) and (cdsupdate.Fields[i-1].tag=1) then
begin
if s1='' then
begin
s1:=Trim(Cdsupdate.Fields[i-1].FieldName);
s2:=Vartosql(Cdsupdate.Fields[i-1].value);
end else
begin
s1:=s1+','+Trim(Cdsupdate.Fields[i-1].FieldName);
s2:=s2+','+Vartosql(Cdsupdate.Fields[i-1].value);
end;
end;
if s1<>'' then
begin
CmdStr:=' Insert into '+pTablename+'('+s1+') values('+s2+')';
end;
end;
usDeleted: begin
s2:='';
for j:=1 to Keylist.Countdo
begin
if s2='' then
s2:=Keylist[j-1]+'='+Vartosql(Cdsupdate[Keylist[j-1]]) else
s2:=s2+' and '+Keylist[j-1]+'='+Vartosql(Cdsupdate[Keylist[j-1]]);
end;
CmdStr:='Delete '+ptablename+' where '+s2;
end;
end;
if CmdStr<>'' then
sqlstr:=sqlstr+CmdStr+';'+chr(13)+chr(10);
//doshowsql(cmdStr);
Cdsupdate.Next;
end;
end;
finally Cdsupdate.Close;
Cdsupdate.Free();
end;
result:=sqlstr;
end;
 
2.单表更新function TRDM.applyupdates(const sqlconn: WideString;
pdelta: OleVariant;
const ptablename, pkeyfields: WideString): WordBool;var cmdstr:widestring;
begin
cmdstr:=gensqls(sqlconn,pdelta,ptablename,pkeyfields);
if cmdstr<>'' then
begin
do
showsql(cmdStr);
if Execsql(sqlconn,Cmdstr) then
result :=true else
result:=false;
end;
end;
 
3.主从表更新function TRDM.MdApplyupdates(const sqlconn: WideString;
pdelta1, pdelta2: OleVariant;
const Ptablename1, Ptablename2, Pkeyfields1, Pkeyfields2: WideString): WordBool;var cmdstr:widestring;
begin
cmdstr:=gensqls(sqlconn,pdelta1,ptablename1,pkeyfields1);
cmdstr:=cmdstr+gensqls(sqlconn,pdelta2,ptablename2,pkeyfields2);
if cmdstr<>'' then
begin
//doshowsql(cmdStr);
if Execsql(sqlconn,Cmdstr) then
result :=true else
result:=false;
end;
end;
 
帮顶,很有学习价值,虽然我看不懂。
 
谢谢高手,可否提供说明啊,真的读起来有点压力!---11:40 我看了,只是跟新在服务端,并没有解决冲突啊!
 
客户端可以设主从表关联,并用以下的方法提交更新function TEM.savedata(pdelta: OleVariant;
ptablename,pkeyfields: WideString):boolean;var mydata:Tclientdataset;
begin
if ConnRemoteServer then
try mydata:=Tclientdataset.Create(EM);
mydata.Data:=pdelta;
if not scon.Connected then
scon.Connected:=true;
result:=scon.AppServer.applyupdates(SqlConnName,mydata.Delta,ptablename,pkeyfields);
//true保存成功 except mydata.Free;
scon.Connected:=false;
result:=false;
end;
end;
function TEM.savemddata(pdelta1,pdelta2: OleVariant;
ptablename1,ptablename2,pkeyfields1,pkeyfields2: WideString):boolean;var data1,data2:TClientDataSet;
begin
if ConnRemoteServer then
try data1:=TClientDataSet.Create(nil);
data2:=TClientDataSet.Create(nil);
data1.Data:=pdelta1;
data2.Data:=pdelta2;
result:=scon.AppServer.mdapplyupdates(SqlConnName,data1.Delta,data2.Delta,ptablename1,ptablename2,pkeyfields1,pkeyfields2);
//true保存成功 finally data1.Free;
scon.Connected:=false;
data2.Free;
end;
end;
 
可否明示具体怎么使用以上函数(先在那个事件,以及先后顺序),遮掩更加好理解,谢谢!
 
你说的这个就是有点复杂,需要考虑:1.客户端用什么进行数据持久?2.传输用什么数据格式表示?3.生成保存的时候的where条件是什么?比如:一个单 update abill set date_str=:data_str where confirm_flag=1 and bill_no = :bill_no--------困难是如果将某些东西放客户端,那么方便是方便了,有点不符合3层如果放服务器段,很难做出通用,方便的东东
 
同意以上意见我想了个方法,一个用户只能登陆一个在线,不同用户不能修改这样就解决了多用户修改的问题了,关键是保存的方法,比如lssgj 写的代码比直接用主从表有区别?
 
测试发现通过 CDSJinHuoDanBiaoShen.First ;
while not CDSJinHuoDanBiaoShen.Eofdo
begin
Case CDSJinHuoDanBiaoShen.UpdateStatus of usUnmodified: Showmessage('Unmodified-'+IntToStr(CDSJinHuoDanBiaoShen.RecNo ));
usModified: Showmessage('modified-'+IntToStr(CDSJinHuoDanBiaoShen.RecNo ));
usInserted: Showmessage('Inserted-'+IntToStr(CDSJinHuoDanBiaoShen.RecNo ));
usDeleted: Showmessage('Deleted-'+IntToStr(CDSJinHuoDanBiaoShen.RecNo ));
end;
CDSJinHuoDanBiaoShen.next;
end;
不能检测到删除的记录啊,如何解?
 
你看看前面的代码:scon.AppServer.applyupdates(SqlConnName,mydata.Delta,ptablename,pkeyfields);客户端传到服务端的其实是Tclentdataset.Delta,你的 CDSJinHuoDanBiaoShen 应该从Tclentdataset.Delta取得数据
 
我是在客户端得数据啊!
 
function vartosql(value: variant): string;
begin
if varisnull(Value) then
Result:='NULL' else
case Vartype(value) of varDate: Result:=Quotedstr(Datetimetostr(VartoDatetime(Value)));
varString,varOlestr: Result:=Quotedstr(Trim(Vartostr(Value)));
varboolean: begin
if Value then
Result:='1' else
Result:='0';
end;
else
Result:=Quotedstr(Trim(Vartostr(Value)));
end;
end;
 
测试发现删除的时候还是不更新啊!如果有关键字段,也是不能更新的!http://www.delphibbs.com/delphibbs/dispq.asp?lid=2330434 以上有主题,不过更多函数没有贴出来,看来必须的放弃了!
 
通过N次测试,发现最好的就是主从表不要有主从关系,直接当主表记录移动的时候,从表跟桌移动,这样有比较明确的办法?
 
我把相关函数都贴出来,注意这里用到了zlib控件,不用也可以,把相关压缩解压代码删了就是,如果不考虑用多帐套,很多你都可以简化function TEM.ConnRemoteServer:boolean;
begin
try if not scon.Connected then
scon.Connected:=true;
result:=true;
except showmessage('连接服务器失败');
result:=false;
endend;
procedure TEM.opendataset(var mydataset:TClientDataSet);
begin
if ConnRemoteServer then
begin
mydataset.Data:=NoCompressedData(scon.AppServer.getdata(SqlConnName,mydataset.CommandText));
scon.Connected:=false;
end;
end;
function TEM.getdata(psql: WideString): OleVariant;
begin
if ConnRemoteServer then
begin
result:=NoCompressedData(scon.AppServer.getdata(SqlConnName,psql));
scon.Connected:=false;
end;
end;
function TEM.savedata(pdelta: OleVariant;
ptablename,pkeyfields: WideString):boolean;var mydata:Tclientdataset;
begin
if ConnRemoteServer then
try mydata:=Tclientdataset.Create(EM);
mydata.Data:=pdelta;
if not scon.Connected then
scon.Connected:=true;
result:=scon.AppServer.applyupdates(SqlConnName,mydata.Delta,ptablename,pkeyfields);
//true保存成功 except mydata.Free;
scon.Connected:=false;
result:=false;
end;
end;
function TEM.savemddata(pdelta1,pdelta2: OleVariant;
ptablename1,ptablename2,pkeyfields1,pkeyfields2: WideString):boolean;var data1,data2:TClientDataSet;
begin
if ConnRemoteServer then
try data1:=TClientDataSet.Create(nil);
data2:=TClientDataSet.Create(nil);
data1.Data:=pdelta1;
data2.Data:=pdelta2;
result:=scon.AppServer.mdapplyupdates(SqlConnName,data1.Delta,data2.Delta,ptablename1,ptablename2,pkeyfields1,pkeyfields2);
//true保存成功 finally data1.Free;
scon.Connected:=false;
data2.Free;
end;
end;
function TEM.execsql(const cmdstr: WideString): boolean;
begin
if ConnRemoteServer then
try if not scon.Connected then
scon.Connected:=true;
result:=scon.AppServer.execsql(SqlConnName,cmdstr);
except result:=false;
scon.Connected:=false;
end;
end;
procedure TEM.DataModuleCreate(Sender: TObject);var myinifile:TInifile;
begin
myinifile := TInifile.Create('./clientset.ini');
SqlConnName:=myinifile.readstring('connset','SqlConnName','jxc36090');
//scon.Host:= myinifile.readstring('connset','ServerIP','127.0.0.1');
scon.address:= myinifile.readstring('connset','ServerIP','127.0.0.1');
scon.Port:= myinifile.ReadInteger('connset','port',211);
scon.ServerGUID:= myinifile.readstring('connset','serverGuid','{DE727235-6681-49C1-880E-2D267346219E}');
scon.ServerName:= myinifile.readstring('connset','servername','RdmServer.RDM');
myinifile.Destroy;
try scon.Connected:=true;
except showmessage('连接参数不正确或者没有启动中间服务器');
end;
setnumericformat;
end;
procedure TEM.VariantToStream (const V: OLEVariant;
Stream : TStream);var P : Pointer;
begin
Stream.Position := 0;
Stream.Size := VarArrayHighBound (V, 1) - VarArrayLowBound (V, 1) + 1;
P := VarArrayLock (V);
Stream.Write (P^, Stream.Size);
VarArrayUnlock (V);
Stream.Position := 0;
end;
procedure TEM.StreamToVariant (Stream : TStream;
var V: OLEVariant);var P : Pointer;
begin
V := VarArrayCreate ([0, Stream.Size - 1], varByte);
P := VarArrayLock (V);
Stream.Position := 0;
Stream.Read (P^, Stream.Size);
VarArrayUnlock (V);
end;
function TEM.CompressedData(NoCompressedData:OleVariant): OleVariant;var M1,M2: TMemoryStream;
begin
Result := Null;
M1 := TMemoryStream.Create;
M2 := TMemoryStream.Create;
try Result := NoCompressedData;
//取得结果集 if Result = Null then
exit;
VariantToStream(Result,M1);
//转换到流 M1.Position := 0;
ZCompressStream(M1,M2,zcDefault);
//压缩流 StreamToVariant(M2,Result);
//转换到变体返回值 finally M1.Free;
M2.Free;
end;
end;
function TEM.NoCompressedData(CompressedData:OleVariant): OleVariant;var M1,M2: TMemoryStream;
begin
Result := Null;
M1 := TMemoryStream.Create;
M2 := TMemoryStream.Create;
try Result := CompressedData;
//取得结果集 if Result = Null then
exit;
VariantToStream(Result,M1);
//转换到流 M1.Position := 0;
ZDeCompressStream(M1,M2);
//解压缩流 StreamToVariant(M2,Result);
//转换到变体返回值 finally M1.Free;
M2.Free;
end;
end;
procedure TEM.xsddCalcFields(DataSet: TDataSet);var myfloat:double;
begin
DataSet.FieldByName('cJE').AsFloat := DataSet.FieldByName('SL').AsFloat * DataSet.FieldByName('DJ').AsFloat ;
end;
procedure TEM.GysZTGetText(Sender: TField;
var Text: String;
DisplayText: Boolean);
begin
if (Sender as TField).AsBoolean=True then
Text:='√' else
Text:=' ';
end;
procedure TEM.ckdbCalcFields(DataSet: TDataSet);
begin
DataSet.FieldByName('cJE').AsFloat := DataSet.FieldByName('SL').AsFloat * DataSet.FieldByName('DJ').AsFloat;
end;
procedure TEM.zhYHLGetText(Sender: TField;
var Text: String;
DisplayText: Boolean);
begin
if (Sender as TField).AsBoolean=True then
Text:='银行' else
Text:='现金';
end;
procedure TEM.srzclbSRLGetText(Sender: TField;
var Text: String;
DisplayText: Boolean);
begin
if (Sender as TField).AsBoolean=True then
Text:='√';
end;
procedure TEM.qtkcbdlxRKLGetText(Sender: TField;
var Text: String;
DisplayText: Boolean);
begin
if (Sender as TField).AsBoolean=True then
Text:='入' else
Text:='出';
end;
procedure TEM.qtkcbdlxTHBZGetText(Sender: TField;
var Text: String;
DisplayText: Boolean);
begin
if (Sender as TField).AsBoolean=True then
Text:='要还' else
Text:='不还';
end;
procedure TEM.ygLZGetText(Sender: TField;
var Text: String;
DisplayText: Boolean);
begin
if (Sender as TField).AsBoolean=True then
Text:='√' else
Text:=' ';
end;
procedure TEM.HpkcaBatchManageGetText(Sender: TField;
var Text: String;
DisplayText: Boolean);
begin
if (Sender as TField).AsBoolean=True then
Text:='√' else
Text:=' ';
end;
procedure TEM.ygYWYBZGetText(Sender: TField;
var Text: String;
DisplayText: Boolean);
begin
if (Sender as TField).AsBoolean=True then
Text:='√' else
Text:=' ';
end;
procedure TEM.ywytzSFBZGetText(Sender: TField;
var Text: String;
DisplayText: Boolean);
begin
if (Sender as TField).AsBoolean=True then
Text:='应收' else
Text:='应付';
end;
 
主从表更新没有问题,以上方法我在一个药品批发软件中用得很成熟的,我是用以下方法从服务端取数和主从关联的,仅供参考:strWhere := 'where Z.RQ between ''' + FormatDateTime( 'yyyy-mm-dd',dtpRqa.Date ) + ''' and ''' + FormatDateTime( 'yyyy-mm-dd',dtpRqb.Date ) + '''' strZbselect:='SELECT Z.*,'+ ' K.BH AS aKHBH, K.MC AS aKHMC, C.MC AS aCKMC, '+ ' y.XM AS aYgmc, B.MC AS aBMMC, a1.uName AS aCzyName,a2.uName AS aFhyname '+ ' FROM XSKDZB Z LEFT OUTER JOIN ' + ' KH K ON Z.KHID = K.ID LEFT OUTER JOIN '+ ' CK C ON Z.CKID = C.ID LEFT OUTER JOIN '+ ' YG y ON Z.YGID = y.ID LEFT OUTER JOIN '+ ' BM B ON Z.BMid = B.ID LEFT OUTER JOIN '+ ' AppUser a1 ON Z.CZYID = a1.ID LEFT OUTER JOIN '+ ' AppUser a2 ON Z.FHYID = a2.ID '+ strWhere + ' order by Z.DJBH';
strMxselect:='SELECT M.*, H.BH as aBH,H.PM AS aPM, h.guige as aGuige,H.DW AS aDW,h.chemicalname,h.QuantityPerUnitPack, h.batchmanage,b.BatchCode AS batchcode,'+ ' dz.DJBH AS cDDBH '+ ' FROM XSKD M LEFT OUTER JOIN HP H ON M.HPID = H.ID LEFT OUTER JOIN '+ ' BatchCodeTable b ON b.id = M.BATCHID left join xskdzb z on z.id=m.zbid LEFT OUTER JOIN '+ ' XSDD d ON d.ID = M.DDMXID LEFT OUTER JOIN '+ ' XSDDZB dz ON d.ZBID = dz.ID '+ strWhere +' order by m.zbid,m.id';
dszb.data:=em.getdata(strZbselect);
dsmx.data:=em.getdata(strMxselect);
Tclientdataset(dsMxs.dataset).MasterSource:=dsZbS;
TClientdataset(dsMxs.dataset).IndexFieldNames:='zbid;id';// 使用这一句造成合计不能用,要用合计功能只能用indexname属性 TClientdataset(dsMxs.dataset).MasterFields:='ID';保存用以下代码: if (dszb.ChangeCount>0) and (dsmx.ChangeCount>0) then
begin
if em.savemddata(dszb.Delta,dsmx.Delta,strZbTableName, strMxtableName,strzbkeyid, strmxkeyid) then
begin
dszb.MergeChangeLog;
dsmx.MergeChangeLog;
end else
Abort;
end else
if dszb.ChangeCount>0 then
begin
if em.savedata(dszb.Delta,strZbTableName,strzbkeyid) then
dszb.MergeChangeLog else
Abort;
end else
if dsmx.ChangeCount>0 then
begin
if em.savedata(dsmx.Delta,strMxtableName,strmxkeyid) then
dsmx.MergeChangeLog else
Abort;
end;
 
太专业,测试号就给分,
 
后退
顶部