上次发了个贴没有人回,自己搞了好久还是不行。现在又来求助于大家了,希望会的朋友不要掖着藏着,谢谢了。服务端
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;弄了一个多星期让我很受打击,会的朋友请说详细些怎么设置,,能发个源码实例将感激不尽.

解决方案 »

  1.   

    可以分成多条update写,加上事务控制
      

  2.   

    一般的原则是join来的DataSet不要用来编辑,BDE/dbexpress都不支持编辑,只能只读。
    而ADO可以编辑,但要求两个表都有主键。
      

  3.   

    由于楼上所说的问题,所以我比较不喜欢用数据敏感,更喜欢自己写保存更新的方法,然后用参数SQL来实现功能。
      

  4.   

    你这个问题也没有表述明白:是不是用三层结构(MIDAS/DATASNAP) 然后想通过一句多表连接的SQL同时更新多张表?如果是这样,可以给你一点建议:Client不用做任何修改,在中间层的那个连结数据库的DataSet的OnApplyRecord事件中写一点更新数据的代码,我这里只是举例,你自己看Help研究一下,注意函数中的Action,很简单的:procedure TEngAppServer.cdTestDataSetApplyRecord(Sender: TOracleDataSet;
      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;如果要添加事务控制,自己修改一下
      

  5.   

    上面的call 修改成为 sSQL1,sSQL2等。如果你用ADO,似乎它默认支持多表自动更新,我不用ADO没有试过,有兴趣自己玩玩.
      

  6.   

    这位兄台,我是用三层MIDAS,服务端 adoquery+provider,客户端 Clientdataset我的意思是这样的,这个系统我以前是二层的,
    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 表中也存在用户名字段。
      

  7.   

    多表关联不是多个表都要更新,我只想更新一个表power,employee表只是获取employee_name来显示而已。
    说白了就是通过left join 来替代计算列方法获取字段employee_name
      

  8.   

    用ADO的数据集控件的话可以直接 更新多表join的数据,但是要求join的表都要有主键,要不然就会产生类似'无法为更新行定位,一些值可能已被更改'的错误.建议:将多表join的sql语句写成视图,然后用ADOTable控件连接到这个视图,最主要的是要保证 join的各个表都要有主键,这个应该是不难实现的.(我一般在每个表中都设有一个自动增长型的主键)
      

  9.   

    谢谢楼上回答,power表只有unique约束,但employee表有主键.
    后来power表我新加了自动id为主键,还是不行啊。可不可以详细的列下步骤具体要怎么做啊?谢谢
      

  10.   

    楼主可以试试 
    数据集就用AdoDataset,设置其commandText属性为楼主所说的SQL语句
    将与之连接的datasetProvider的resolvetodataset属性设成TRUE,再将updatemode设成upWhereKeyOnly,但是这样可能会影响另外用户对数据的更改还不行的话,楼主不妨贴出完整的SQl语句 让大家分析下
      

  11.   

    按照这个描述,楼主不必用复杂的join语句了,参考李维的<Delphi 5.x ADOMTSCOM+高级程序计篇>网上游下载)一书 
    第二章 34页 2-2-1 计算字段一节,应该就可以解决楼主的问题了ps 我也在学习Delphi的三层开发 留个QQ方便交流:277940225
      

  12.   


    你的代码没有写完整,所以更新不了。
    对于通过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,找管理员大海;
      

  13.   

    十分感谢 
    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;更新类似,没有写。