问题一: 我在三层结构下做主从表,在服务器用的是巢状结构来关联主从结构,客户端用clientdataset的insert和applyupdates来提交,但主表数据可以正常提交写入数据库,但从表毫无动静,也就是说从表能写入clientdateset的缓存,但不能写入数据库,不管是用applyupdata(-1)还是用applyupdate(0)都是这样,请教各位老大这是什么原因?
问题二:有时候用clientdataset的edit方法和delete方法修改数据的时候程序会出错,提示说:不能执行该操作,该记录已被别人修改,(但实际上只有我一个人使用,并没有修改数据),不知这是什么原因?
我只是用clientdataset的insert,edit 再applyupdata的方法对一个数据表table1的abc纪录进行操作,然后由于某种需要,我用另一个clientadaset(专门执行sql语句的公用clientdataset)的commandtext执行一个更新此table1表中abc纪录的操作而已。
问题三:在问题二中,是不是用了clientdataset的insert,edit方法以后,就最好不要再用commandtext方法提交数据阿?我向别人请教,他说最好要么一直用clientdataset的insert,edit方法,要么一直用commandtext提交insert,update语句
问题四:用clientdataset的方法增删改数据时,有时候会提示:Cannot find record,No key specified. 我不知道这是什么原因引起的?有人说要加主键,但我的数据库表已经有主键,有人说是因为我的数据库表中有一个字段是设定自动增一的,又有的人说只需设置datasetprovider的某个属性就行了,莫衷一是,请各位先悟道者给个权威说法  :)

解决方案 »

  1.   

    1)应该用applyupdate(0);出现不能提交的原因,主要是Delphi无法解析主从表的表名。只要在DataSetProvider中的OnGetTableName事件中写代码:
    if (DataSet = qryMaster) then
      TableName := 'MasterTableName'
    else if (DataSet = qryDetail) then
      TableName := 'DetailTableName';
    这样就可以了。
      

  2.   

    2)产生错误的原因是Delphi自动生成的SQL语句不能精确定位记录。主要是由于你的表中有浮点,日期,MEMO等类型造成的。(浮点的二进制存储,造成数值不精确)
    消除这个的方法是,给Query中每个Field的ProviderFlags设置属性。只有主键才有pfInWhere,其他的去除就可以了。
    3)commandtext纯属多余,可以不用。用上面的方法可以不写一行代码,只要点点鼠标就做到了。
    4)取消该Field的pfInUpdate就可以了。
      

  3.   

    chechy(为程序而奋斗) ,在delphi5中应该可行的
      

  4.   

    Midas进阶 - 巢状数据表实现Master/Detail的应用系统
    首先,还应该知道Master/Detail到底是什么关系;
    其次,要明白什么是巢状数据表是什么意思;
    Master/Detail这种主细关系,只要做过数据库程序的人应该都知道,在C/S结构里随时都是,很长见的;
    巢状数据表应该如何理解?它就是分布式系统和C/S结构的对于Master/Detail的不同操作机制而引申的;从客户端而言,在C/S结构中,Master/Detail可以用两个数据集组件分别取出表中具有Master/Detail关系的数据,但是在分布多中,对于客户端而言,我们只关心Master的数据集的处理方式,而如何对应Master/Detail则是在应用服务方面进行操作,当我们正确连接了客户端的MasterClinetDataSet(TClientDataSet)时,其所对应的Detail就会随着数据包自动的下载到客户端,这就是巢状数据表的初步解释;而它的正真定义应该是:当数据封包在封装数据时,这个数据封包甚至能够把一个数据集封装起来;
    下边用一个例程来说明:
    应用服务端:在应用服务器上要实现两个数据组件的M/D(Master/Detail,以后用M/D代替)的关系,可以用TTable or TQuery ,TADOQuery or TADOTable or TADODataSet组件都完全可以;这儿要建立它们的M/D关系,和C/S中的完全一样(中间需要通过一个TDataSource连接);客户端:在客户端只需要用一个TClientDataSet来接收数据,这儿更能体现“巢状“,所谓的接收数据是从应用服务端下载数据,是将M/D数据都下载下来,这后,Delphi可以自动的识别这是一个M/D结构的;此时需要将另一个TClientDataSet组件加进来,而且要设置它的DataSetField为下载数据的TClientDataSet中的Detail数据字段(可以这样理解);但不应该忘记了要执行TClientDataSet.Fetch Params,只有这样才可以实现Detail -> TClientDataSet的DataSetField的设置;设置完成后,就可以正确运行了;
    下边我给出设置实例,供参考
    应用服务端窗体文件如下:
    object DMF_LX: TDMF_LX
      OldCreateOrder = False
      Left = 192
      Top = 107
      Height = 409
      Width = 570
      object ADOConnection1: TADOConnection
        Connected = True
        ConnectionString = 
          'Provider=SQLOLEDB.1;Persist Security Info=True;User ID=sa;Initia' +
          'l Catalog=pubs;Data Source=(local)'
        /////数据连接字符串,有必要看一看;
        LoginPrompt = False
        Provider = 'SQLOLEDB.1' 
        Left = 72
        Top = 32
      end
      object MADODataSet: TADODataSet
        Connection = ADOConnection1
        CursorType = ctStatic
        CommandText = 'select * from publishers'
        Parameters = <>
        Left = 64
        Top = 104
        object MADODataSetpub_name: TStringField
          FieldName = 'pub_name'
          Size = 40
        end
        object MADODataSetcity: TStringField
          FieldName = 'city'
        end
        object MADODataSetstate: TStringField
          FieldName = 'state'
          FixedChar = True
          Size = 2
        end
        object MADODataSetcountry: TStringField
          FieldName = 'country'
          Size = 30
        end
      end
      object DADODataSet: TADODataSet
        Connection = ADOConnection1
        CursorType = ctStatic
        CommandText = 'select * from titles'
        DataSource = MDDataSource
        IndexFieldNames = 'pub_id'/////////这儿建立关系;
        MasterFields = 'pub_id'  /////////这儿建立关系;
        Parameters = <>
        Left = 168
        Top = 104
      end
      object GADODataSet: TADODataSet
        Connection = ADOConnection1
        Parameters = <>
        Left = 272
        Top = 104
      end
      object MDDataSource: TDataSource
        DataSet = MADODataSet
        Left = 264
        Top = 32
      end
      object MDataSetProvider: TDataSetProvider
        DataSet = MADODataSet
        Constraints = True
        Left = 64
        Top = 168
      end
      object DDataSetProvider: TDataSetProvider
        DataSet = DADODataSet
        Constraints = True
        Left = 176
        Top = 168
      end
      object GDataSetProvider: TDataSetProvider
        DataSet = GADODataSet
        Constraints = True
        Options = [poAllowCommandText]
        Left = 280
        Top = 168
      end
    end客户端窗体设计:
    由于太多,我只取重要的一部分
    object Form2: TForm2
      Left = 176
      Top = 154
      BorderIcons = [biSystemMenu, biMinimize]
      BorderStyle = bsSingle
      Caption = '数据更新'
      //////窗体描述;
      TextHeight = 13
      object DBGrid1: TDBGrid
        Left = 0
        Top = 25
        Width = 440
        Height = 120
        Align = alTop
        DataSource = MDataSource
        TabOrder = 0
        TitleFont.Charset = DEFAULT_CHARSET
        TitleFont.Color = clWindowText
        TitleFont.Height = -11
        TitleFont.Name = 'MS Sans Serif'
        TitleFont.Style = []
      end
      end
      object DBGrid2: TDBGrid
        Left = 0
        Top = 168
        Width = 440
        Height = 180
        Align = alBottom
        DataSource = DDataSource
        TabOrder = 2
        TitleFont.Charset = DEFAULT_CHARSET
        TitleFont.Color = clWindowText
        TitleFont.Height = -11
        TitleFont.Name = 'MS Sans Serif'
        TitleFont.Style = []
      end
      object Button1: TButton
        Left = 350
        Top = 144
        Width = 89
        Height = 25
        Caption = '数据更新'
        TabOrder = 4
        onClick = Button1Click
      end
      object DCOMConnection1: TDCOMConnection
        ServerGUID = '{49477FD4-DD51-4F8B-ACD4-404A6C07D92F}'
        ServerName = 'Server.DMF_LX'
        ComputerName = 'Zsh'
        Left = 16
        Top = 64
      end
      object MClientDataSet: TClientDataSet
        Aggregates = <>
        DisableStringTrim = True
        Params = <>
        ProviderName = 'MDataSetProvider' ////下载数据
        RemoteServer = DCOMConnection1
        Left = 48
        Top = 64
        object MClientDataSetpub_id: TStringField
          FieldName = 'pub_id'
          ProviderFlags = [pfInUpdate, pfInWhere, pfHidden]
          Visible = False
          FixedChar = True
          Size = 4
        end
        object MClientDataSetpub_name: TStringField
          FieldName = 'pub_name'
          Size = 40
        end
        object MClientDataSetcity: TStringField
          FieldName = 'city'
        end
        object MClientDataSetstate: TStringField
          FieldName = 'state'
          FixedChar = True
          Size = 2
        end
        object MClientDataSetcountry: TStringField
          FieldName = 'country'
          Size = 30
        end
        object MClientDataSetDADODataSet: TDataSetField
          FieldName = 'DADODataSet'
     //只有执行了Fatch Params时,才可以在这儿看到它,另一个数据集也才可以取这个下载的字段;
          ProviderFlags = [pfInUpdate, pfInWhere, pfHidden]
          Visible = False
        end
      end
      object MDataSource: TDataSource
        DataSet = MClientDataSet
        Left = 80
        Top = 64
      end
      object DClientDataSet: TClientDataSet
        Aggregates = <> ///////计算字段;
        DataSetField = MClientDataSetDADODataSet
        Params = <>
        Left = 56
        Top = 216
        object DClientDataSettitle: TStringField
          FieldName = 'title'
          Size = 80
        end
        object DClientDataSettype: TStringField
          FieldName = 'type'
          FixedChar = True
          Size = 12
        end
        ////这一部分是数据字段定义
        object DClientDataSetpubdate: TDateTimeField
          FieldName = 'pubdate'
        end
      end
      object DDataSource: TDataSource
        DataSet = DClientDataSet
        Left = 88
        Top = 216
      end
      object GClientDataSet: TClientDataSet
        Aggregates = <>
        Params = <>
        RemoteServer = DCOMConnection1
        Left = 232
        Top = 216
      end
    end当你要更新时,只用执行ApplyUpdates(MasError)就中;
    只需要执行Master ClinetDataSet的,不用执行Detial的,原因自己可以分析;
    如果不明白,可以给我发短消息;
      

  5.   

    问题一:你需要针对每个字段指明是更新哪个表。
    问题二:可能是有触发器改动了你的数据,也就是说,
           现在ClientDataSet中的数据已经不是数据库中的数据,
           加两句:
           ClientDataSet.Close;
           ClientDataSet.Open;
           就可以了。问题三:建议你统一使用ApplyUpdate。
    问题四:参考二。
    我的删除和保存代码://~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //CustomClientDataSet.Delete(远程更新(三层结构))********************************
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    function Data_SafeDelete(CustomClientDataSet: TCustomClientDataSet; DBGrid: TDBGrid = nil): Boolean; overload;
    begin
      if DBGrid<>nil then DBGridDelete(DBGrid);
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      Result := CustomClientDataSet.ApplyUpdates(0)=0;
      if not Result then Abort;
    end;//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //CustomClientDataSet.Save(远程更新(三层结构))**********************************
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    function Data_SafeSave(CustomClientDataSet: TCustomClientDataSet; WinControl: TWinControl = nil; vRefrseh: Boolean = false): Boolean; overload;
    var SavePlace: TBookMark;
    begin
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      //保存数据库指针记录**********************************************************
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      SavePlace := CustomClientDataSet.GetBook;
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      Result := CustomClientDataSet.ApplyUpdates(0)=0;
      if not Result then Abort;
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      //指定控件聚焦****************************************************************
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      if WinControl<>nil then WinControl.SetFocus;
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      //刷新数据库和返回并释放书签**************************************************
      //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      with CustomClientDataSet do
      try
        if vRefrseh then
        begin
          Close;
          Open;
          GotoBook(SavePlace);
        end;
      finally
        FreeBook(SavePlace);
      end;
    end;
         
      

  6.   

    问题二:因为你那个CommandText的操作已经修改了服务器上的abc记录,而ClientDataset缓冲里还是老的数据,这时如果更新ClientDataSet的数据,因为原数据与服务器不一致,就会出这个错.最好还是不要这样干。
    问题三:原因如上
    问题四:你在数据库中设置的主键仅是数据库的主键,ClientDataset并不知道,而有此操作需要主键,所以你必须在向ClientDataSet提供数据时指定主键,方法就是用ProviderFlags:
    我一般是在与DataSetProvider相连的DataSet(如ADOQuery/ADODataSet...)的AfterOpen事件中用:FieldByName( 'ID' ).ProviderFlags := [pfInKey]来指定主键即可。另外,在多层应用中最好不要用Identity类型和Trigger,这种服务端的数据变化会导致多层数据更新的很多麻烦