上次发了个贴没有人回,自己搞了好久还是不行。现在又来求助于大家了,希望会的朋友不要掖着藏着,谢谢了。服务端
SQL 'SELECT A.*,B.Employee_name,C.Document_Name FROM power A LEFT JOIN XX ...'更新或保存就报错,提示说“找不到记录,字段没有在列表中”我上网找了好多资料,把pfInwhere也设为False了,
在服务端也了代码procedure TKaoqinAppServer.qryPowerAfterOpen(DataSet: TDataSet);
begin
qryPower.Recordset.Properties['Unique Table'].Value :='Power';
end;procedure TKaoqinAppServer.P_PowerGetTableName(Sender: TObject;
DataSet: TDataSet; var TableName: String);
begin
TableName:='Power';
end;弄了一个多星期让我很受打击,会的朋友请说详细些怎么设置,,能发个源码实例将感激不尽.
而ADO可以编辑,但要求两个表都有主键。
Action: Char; var Applied: Boolean; var NewRowId: String);
var
sSQL1,sSQL2:String;
begin case Action of
//更新数据
'U': Begin
sSQL1 :='Update Target_table1 set (Filed1,Field2,.....)=(:Value11,Value12,....);
sSQL2 :='Update Target_table2 set (Filed1,Field2,.....)=(:Value21,Value22,....);
end;
//删除数据
'D': Begin
sSQL1 :='Delete Target_table1 where Field1=:value1';
sSQL2 :='Delete Target_table2 where field1=:value2';
end;
//.....等等,根据自己的情况添加
else
Begin
sSQL1:='';
sSQL2:='';
end;
end; if Call <> '' then
begin
with updateDataSet do
begin
Clear;
SQL.Add('begin');
SQL.Add(' ' + sSQL1 + ';');
SQL.Add('end;');.... 记得设定参数,赋值 .......... Execute;
end;
end;
// Notify to the dataset the we have applied the record
Applied := True;end;如果要添加事务控制,自己修改一下
Adoquery1的SQL:='select * from Power'
但power 这个表只有员工编号,没有员工姓名,所以我要关联employee 表来获取employee_name这个字段
我之前 employee_name这个字段我以前是通过Adoquery1的计算列来取得的.但我现在改为三层了,并不想用"计算列"方法,效率低大家都知道的。所以我改了SQLSELECT A.*,B.employee_name FROM power A LEFT JOIN employee B ON A.gh=B.gh
但新增和修改都保存失败。这次我应说明白了吧,我相信开发人员都会遇到我一样的问题,因为我们的表
结构是3NF的,不可能在employee 表中有用户名这一字段,在power 表中也存在用户名字段。
说白了就是通过left join 来替代计算列方法获取字段employee_name
后来power表我新加了自动id为主键,还是不行啊。可不可以详细的列下步骤具体要怎么做啊?谢谢
数据集就用AdoDataset,设置其commandText属性为楼主所说的SQL语句
将与之连接的datasetProvider的resolvetodataset属性设成TRUE,再将updatemode设成upWhereKeyOnly,但是这样可能会影响另外用户对数据的更改还不行的话,楼主不妨贴出完整的SQl语句 让大家分析下
第二章 34页 2-2-1 计算字段一节,应该就可以解决楼主的问题了ps 我也在学习Delphi的三层开发 留个QQ方便交流:277940225
你的代码没有写完整,所以更新不了。
对于通过ID连接其他表,然后查询出名称的更新,更新时需要设置一些其他附加信息,例如:更新模式、表的主键、哪些字段不需要更新等,如果不设置默认是更新所有字段的,具体解决方法:1、更新模式:DataSetProvider的UpdateMode属性设置为:upWhereKeyOnly,这种设置要求你的表有个主键。2、DataSetProvider的BeforeUpdateRecord事件中,加入一下代码:
with DeltaDS.FieldByName('id') do //这个是主键,你根据需要修改
ProviderFlags := ProviderFlags + [pfInKey];
with DeltaDS.FieldByName('Employee_name') do
ProviderFlags := ProviderFlags - [pfInUpdate];
with DeltaDS.FieldByName('Document_Name') do
ProviderFlags := ProviderFlags - [pfInUpdate];
这段代码表明:字段:Employee_name、Document_Name不需要更新;3、在DataSetProvider所连接的AdoQuery的AfterOPen时间中加入
qryPower.Properties['Unique Table'].Value := 'Power';
说明:你的代码好像多了个.Recordset.上面3点设置都是在服务端的,上面这几点就可以更新了,如果还有问题的话,可以加入QQ高级群:9642802,找管理员大海;
Jack_Yin(撒哈拉教徒) 和 wxsan(未来征程) 两位的热心关注与解答.共发了两个贴,今天总算把问题搞定了。
其实也怪我没有表达清楚,我的目的就是用left join 来代替计算列取得另一表的字段名而已。这个问题困扰我有十几天了,昨天晚上终于发现问题所在了.就是我的power 表中有字段 Read,Delete等
MSSQL关键字,所以无论你怎么更新都会报错。现在说下我的解决方案提供给那些新手们,希望对你们有些启示。1:既然发现power表中有MSSQL的关键字,则需重新命名
在SQL查询分析器中,sp_rename 'power.read','read1','Column'
sp_rename 'power.delete','delete','Column'2:应用服务器上
A:qryPower(Adoquery控件)的AfterScroll事件中写入
qryPower.Recordset.Properties['Unique Table'].Value :='Power';
(wxsan(未来征程):我不明白这里多个dataset和少个dataset有什么区别。我一直是这样用的)B:P_Power(DataSetProvider控件) 的GetTableName事件中写入 tablename:='Power';C:P_power:的更新模式我选的是 "upWhereChanged"
在这里我说下我的理由:如果选"upWhereKeyOnly"的话,那么你的表一定要有主键,象我power表只有Unique约束(工号,档案号,权限等级),所以必须要新增自动ID做主键,以后数据导入不好操作。而"upWhereChanged"的好处是哪些值改变,就更新哪引些值。这时会有朋友出来质问我“如果这个表有重复数据怎么办?”我想至少我的表结构是不会出现这种情况的,每个表都应有主键或unique约束吧。3:客户端
Cpower(ClientDataset控件)设置
A.把Employee_name、Document_Name字段的ProviderFlags全设为空,并全设为只读B:其它所有字段的ProviderFlags的pfinwhere 设为false。终于大功告成,可以新增和更新了.太高兴了这个问题真的搞了我好久,让我好没信心。
为此我还写自动生成SQL Var
intI,intJ:integer;
arrFieldname,arrFieldValue:OleVariant;
strSql,strFieldName,strFieldVaule:String;
BEGIN
intJ:=0;
arrFieldName:=varArrayCreate([0,Dbgrid1.FieldCount-1],varvariant);
arrFieldValue:=varArrayCreate([0,Dbgrid1.FieldCount-1],varVariant);
For intI:=0 To Dbgrid1.FieldCount -1 Do
Begin
If Pos(Dbgrid1.Columns[intI].FieldName,'Employee_Name,Archives_Name')<=0 Then
Begin
arrFieldName[intJ]:=Dbgrid1.Columns[intI].FieldName ; If Dbgrid1.Fields[intI].DataType =ftDateTime Then
Begin
arrFieldValue[intJ]:=IfThen(CPower.fieldbyname(Dbgrid1.Columns[intI].FieldName).AsDateTime<>null,quotedstr(CPower.fieldbyname(Dbgrid1.Columns[intI].FieldName).AsString),'null') ;
End; If ((Dbgrid1.Fields[intI].DataType =ftBCD) OR (Dbgrid1.Fields[intI].DataType=ftInteger)) Then
Begin
arrFieldValue[intJ]:=IfThen(CPower.fieldbyname(Dbgrid1.Columns[intI].FieldName).AsFloat>=0,CPower.fieldbyname(Dbgrid1.Columns[intI].FieldName).AsString ,'null') ;
End; If Dbgrid1.Fields[intI].DataType =ftString Then
Begin
arrFieldValue[intJ]:=IfThen(CPower.fieldbyname(Dbgrid1.Columns[intI].FieldName).AsString <>'',quotedstr(CPower.fieldbyname(Dbgrid1.Columns[intI].FieldName).AsString ),'null') ;
End; If Dbgrid1.Fields[intI].DataType =ftBoolean Then
Begin
arrFieldValue[intJ]:=IfThen(CPower.fieldbyname(Dbgrid1.Columns[intI].FieldName).AsString ='true','1','0') ;
End;
Inc(intJ);
End;
End;
//新增
If Cpower.State In [dsInsert] Then
Begin
For intI:=0 To intJ-1 Do
Begin
strFieldName:=strFieldName+arrFieldName[intI]+','
End;
For intI:=0 To intJ-1 Do
Begin
strFieldVaule:=strFieldVaule+arrFieldValue[intI]+','
End;
strFieldName:=Leftstr(strFieldName,length(strFieldName)-1);
strFieldVaule:=Leftstr(strFieldVaule,length(strFieldVaule)-1);
StrSql:='INSERT INTO power('+strFieldName+') '+' Values('+strFieldVaule+')';
CPower.commenttext:=strsql;
CPower.execsql;
END;更新类似,没有写。