在三层中,自增字段(id),在新增完数据后,需要返回id,用delphi的数据感知控件,能实现吗?

解决方案 »

  1.   

    好象不行. 用 GUID 做主键
      

  2.   

    在新增完数据后,其实自增字段(id)已经有值了可以这样写代码,获取了
    myid = table.FieldByName('id').AsInteger;数据感知控件,刷新一下状态,应该也能获取得到了
      

  3.   

    楼上的方法有两层时,用 ADO 控件时可行.
      

  4.   

    如果只是增加一条记录,则不用理会自增字段的值,也不用给它赋值(当它不存在),记录增加到数据库后,数据库会自动给该字段赋值。如果是多条记录的情况,如使用TClientDataSet控件,先在客户端增加了多条记录之后,再一次性ApplyUpdate更新的情况。
    1、定义一个全局Integer变量AutoID,初始值为-1。
    2、在TClientDataSet的BeforeInsert事件中,把AutoID值赋给当前记录的自增加字段,然后Dec(AutoID)。
    3、在与TClientDataSet相关联的TDataSetProvider.BeforeUpdateRecord事件中编写:
      if UpdateKind = ukInsert then
        DeltaDS.FieldValues['AutoFieldName'] := Null; 
    4、TClientDataSet.ApplyUpdates(0);
    5、TClientDataSet.Refresh;
    6、OK了编程思路就是上述,取自Borland Developer Network,应该是高手的方法吧。
      

  5.   

    Sql server 自增加字段 和 Oracel 序列,都可以返回最后的值。找找资料
      

  6.   

    我的数据库设计原则:
      表不设主键=等死
      字段设置默认值(包含自增字段)=找死 (3层用中使用ClientDataset时)
      

  7.   

    用存储过程实现才是最保险的方法
    如:
    CREATE  PROCEDURE aaa
    @AutoID  int OutPut
    as
    inser into a(a)values('a')
    set @AutoID=SCOPE_IDENTITY()     //请看SCOPE_IDENTITY()的帮助就清楚了,它能解决多用户同时操作而造成不能取得最新的正确id的问题
      

  8.   

    这个问题前几天才碰到,一句话,不能使用自增字段,如果你使用ClientDataSet,并且需要用到这个ID的时候,就是不能用,必须重新设计数据库。
    参见http://community.csdn.net/Expert/topic/5070/5070916.xml?temp=.4269068
      

  9.   

    如果实在想用自增ID实现,当然能实现,只是效率不会高,至少网络服务端与客户端,增加了回传ID这一步,增大了网络的开销。在三层中是应该毕免这种情况的
      

  10.   

    自增长本来就是 MS sql server 专有的嘛, 其它的用生成器楼上的 CDS.ApplyUPdate 后 CDS.Refresh 当然就可以用自增长的, 就是最后 Refresh 是不是可以接受的问题DSP 有 propogateChanges 选项, DSP.ApplyUpdate 的返回值就是所有的在 Before&After Update Record 时的 Field.NewValue(用 CDS 的会自动直接合并), 关键就是取得 autoID 了个人觉得是没办法, 除非不用 autoID, autoID 的功能实际上很容易仿真, SELECT MAX(ID) FROM TABLE, 不过这样可能在多 CPU 和并发量大的时间产生 key 冲突, 数据是没错, 偶尔一个用户会增加失败生成器功能也很容易仿真的就是建另一个表作为生成器CREATE TABLE [KeyValues] (
    [Name] [varchar] (20),
    [CurrentValue] [int] 
    ) ON [PRIMARY]CREATE PROCEDURE getid @ID INT OUTPUT, @NAME VARCHAR(20) AS
    BEGIN
      UPDATE KEYVALUES SET CURRENTVALUE = CURRENTVALUE + 1 WHERE NAME = @NAME
      SELECT @ID = CURRENTVALUE FROM KEYVALUES WHERE NAME = @NAME  IF @ID IS NULL SET @ID = -1
    END
      

  11.   

    三层中解决自增ID的方法,早就有范式,也不存在回传ID,增大网络开销的顾虑,看看人家在2002年就提出的解决方法吧:http://bdn.borland.com/article/20847
      

  12.   

    用AUTOID不方便之处以上好象解决的都不理想,没有解决到关键上。在项目和产品中,经常为了操作方便,主从表同时增加新数据。如果在主表中新增加一些数据,对应这些主表新数据,又新加了一些子记录。由于主表在提交之前,不能确定主键ID。而子表此时又需要引用主表的ID。
    这样的问题上面好象都没有解决吧。
    虽然能解决,解决方法也不是一种,但这些都增大了网络的开销或增加了开发难度。但根源是一个自动增长带来了。
      

  13.   

    http://bdn.borland.com/article/20847
    解决主从表的方法好象有点问题,BeforeUpdateRecord是在一个事务内部执行的,在此得不断的close,open好象不太合适。
      

  14.   


    CREATE  PROCEDURE aaa
    @AutoID  int OutPut
    as
    inser into a(a)values('a')
    set @AutoID=SCOPE_IDENTITY()     //请看SCOPE_IDENTITY()的帮助就清楚了,它能解决多用户同时操作而造成不能取得最新的正确id的问题
      

  15.   

    服务端实现, 然后回送新的 ID, 比方, CDS 中有 id 字段是 key,sql server 去掉自增, 强调不用自增1.在客户机 CDS.BeforePost 代码中写到, 形式如下var
      XID: Integer;  // 定义在之外哈, 这个初值是 0
    procedure XXXXXXXXBeforePost(DataSet: TDataSet);
    begin
      if DataSet.State = dsInsert then
      begin
        InterlockedDecrement(XID);
        DataSet.FieldValues['id'] := XID; 给一个负值在 key 上, 多记录新增不出错(显示上)
      end;
    end;
    2.在服务器的 BeforeUpdateRecord 代码中写到
    XXXDSPBeforeUpdateRecord(Sender: TObject; SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;  UpdateKind: TUpdateKind; var Applied: Boolean);
    begin
      DeltaDS.FieldByName('ID').NewValue := GetId('xxx.Id');
    end;3. get 过程如下
    function TDictionaryDM.GetId(Name: string): Integer;
    begin
      这里用的是存贮过程, 也可以用一个 Query实现
      getid.Parameters.ParamValues['@NAME'] := Name;
      getid.ExecProc;
      result := getid.Parameters.ParamValues['@ID'];  if result = -1 then
        raise Exception.Create(SKeyNameNotExist);
    end;4. 存贮过程如下
    CREATE PROCEDURE getid @ID INT OUTPUT, @NAME VARCHAR(20) AS
    BEGIN
      UPDATE KEYVALUES SET CURRENTVALUE = CURRENTVALUE + 1 WHERE NAME = @NAME
      SELECT @ID = CURRENTVALUE FROM KEYVALUES WHERE NAME = @NAME  IF @ID IS NULL SET @ID = -1
    END4.表结构如下
    CREATE TABLE [KeyValues] (
    [Name] [varchar] (20),
    [CurrentValue] [int]
    ) ON [PRIMARY]
      

  16.   

    为什么不用自增, 你看了这么多相关后估计也知了,就是说用了自增就只能以 refresh cds 的方法取得正确的数据 id主从表嘛在服务端绑定问题就有点麻烦了, 
    1. 主表 id 修改, 这个问严重提高复杂度, 一般让主表 id 新增后不能改, 实际应用也比较多是如此2. 在 1 的前提下(并不绝对), 主表 id 确定, 从表是 id 新增的
       在 DSP 的 UpdateData 事件中写到
    var
      ChildCDS: TClientDataSet;
    begin
      // 代码非我正式在用的, 主要为说明问题
      ChildCDS := TClientDataSet.Create(nil);
      try
        while not DataSet.Eof do
        begin
          if DataSet.UpdateState = dsInsert then
          begin
            DataSet.Next; 是新增的由 3. 去处理
            continue;
          end;      ChildCDS.DataSetField := DataSet.FieldByName('DataSetField比方') as TDataSetField;
          ChildCDs.First;
          while not ChildCDS.Eof do
          begin
            if ChildCDS.UpdateStatus = usInsert then
              ChildCDS.FieldByName('ref_id(比方)').NewValue := DataSet.FieldByName('id').OldValue; // 因为主表 id 不可改
              // 要用 NewValue/OldValue, 不能用 Value, 这个DataSet 是 DeltaDS 不存在当前值
              // 还有另一些可能性, 不好列举, 实际情况而定, 主表 id 可改就得在下面多写点
            ChildCDS.Next;
          end;      DataSet.Next;
        end;
      finally
        ChildCDS.Free;
      end;
    end;
      

  17.   

    3. 在 2 的前提下, 主表 id 新增, 从表 id 也是新增, 前提这些 id 都是生成的在 dsp 的 beforeupdaterecord 事件中写到var
      NewInsertedId: Integer;
    XXXDSPBeforeUpdateRecord(Sender: TObject; SourceDS: TDataSet; DeltaDS: TCustomClientDataSet; 
    begin
      if SourceDS = 主表Query then
      begin
        if UpdateKind = ukInsert then
        begin
          NewInsertedId := GetId('xxx.Id');
          DeltaDS.FieldByName('ID').NewValue := NewInsertedId;
        end
        else NewInsertedId := -1; 
      end
      else if (SourceDS = 从表Query) and (NewInsertedId <> -1) then
        DeltaDS.FieldByName('Ref_ID').NewValue := NewInsertedId;
    end;
    end;这个是个函数入口得有点头晕,
      

  18.   

    主要是讨论自增ID带来的负作用。处理主从表极为不方便。
    Cassava(车超) 说主从表同时增加数据时很简单,严重不同意。
    客户端不能每增加一条数据就取一次ID,这和两层没什么区别,如果实现公文包离线工作,这样设计就是找死。
    UpdateRecord事件是在一个事务内部反复循环执行,在这里不能为了取ID,出现CLOSE,OPEN,以及执行SQL等语句。除非为这单独建立一个独立的数据连接,但这严重破坏了事物的作用,也大大的降低了性能,弄不好就执行不过去。总之这里不能随意操作数据库。如果提交前主从表的关联键就已确定,以上这些问题就全部不存在了。
    用GUID代替自增ID在开发上要容易的多
      

  19.   

    我在第二楼就回答了.用 GUID
      

  20.   

    Cassava(车超)你那样只是取得ID而已,麻烦的地方是什么时候取,取出来了,如何去用。
      

  21.   

    从头看到尾,终于完了,看累了,我就来总结一下吧!1.三层中别用AutoID, 这是硬道理.
    2.考虑效率问题,不赞同用GUID号做位key,毕竟字符串的比较要花时间的.
    3.保证唯一key,可以另建立一张表tbl_mainkey(key_name varchar(10), num int)
      每次从num里拿号,然后把num+1,就不会重复了.
      

  22.   

    Cassava(车超)  的方法是正解,其他方法都是绕圈子或回避问题的主要矛盾。
    GUID也是可行的,但太长,没有办法的情况下可以考虑。
      

  23.   

    主要是讨论自增ID带来的负作用。处理主从表极为不方便。
    Cassava(车超) 说主从表同时增加数据时很简单,严重不同意。
    客户端不能每增加一条数据就取一次ID,这和两层没什么区别,如果实现公文包离线工作,这样设计就是找死。
    ==========================================
    这段话有问题,添加数据主要由中间层来处理,这也符合多层开发的数据逻辑和企业逻辑,而不是客户端直接让数据库执行它的SQL语句。
      

  24.   

    madyak(无天) 
    我觉得我的方法绝对简单,其实我觉得我已经写出了关键,也正确地回答了楼主的问题,
    就算延伸到主从表,看了我的代码我想一般的程序员都应该知道怎么做了,madyak(无天)你毕竟是两个星星的的人物,难道就真的不会吗?或还是象前面一样装看不懂呢?如果是装不懂的,我再说破嘴也没用
    代码我觉得没什么好贴的了,因为基本上不需要什么代码就基本上能实现了,相信madyak(无天)你是会了的,我也没什么好说的了
      

  25.   

    Cassava(车超) 不要这样说好吗?来得实际的,弄点主从表关联的代码。如果不需要代码,把关键属性设置说说也行呀,不能说空话吧。
      

  26.   

    "加重字段设置默认值(包含自增字段)=找死 (3层用中使用ClientDataset时)"
    有些字段不设默认值才是找死;"用GUID代替自增ID在开发上要容易的多"
    GUID会产生效率问题,而且数据库主健关联一旦复杂.这种方式显示非常恶劣;特别某些
    要做ORM开发的,会被那个长长的唯一码搞死;
    以下回答应该是比较佳的解法;
    "
    从头看到尾,终于完了,看累了,我就来总结一下吧!1.三层中别用AutoID, 这是硬道理.
    2.考虑效率问题,不赞同用GUID号做位key,毕竟字符串的比较要花时间的.
    3.保证唯一key,可以另建立一张表tbl_mainkey(key_name varchar(10), num int)
      每次从num里拿号,然后把num+1,就不会重复了.
    "
    只是要补充一点每访问一次数据库可以取N个ID回来;
      

  27.   

    我觉得GUID用不用并不是关键,关键是在客户端,起关联作用的键,在提交前就要确定下来,至于如何确定根据具体情况决定。提交前用不确定的值做关联确实增加了很大的负担。
      

  28.   

    Cassava(车超) 我很纳闷,你做过三层没有?你返回到哪去,客户端还是服务端?
      

  29.   

    Cassava(车超) 我很纳闷,你做过三层没有?你返回到哪去,客户端还是服务端?
    你是不是没做过三层呀?
    觉得你没有客户端和服务端的概念
    ----------------------------------------
    虽然水平没你高了,但请不要怀疑我没有做过三层啊返回客户端
      

  30.   

    回传ID到客户端的当然是先添加主表记录,从表可以在客户端添加完了,再更新到数据库。如果想主从表都在客户端添加完了,再一次性提交到服务器更新到数据库也是可以实现的
    先输完主从表数据,然后写一个主从表更新的接口方法把数据传到服务器,然后服务器先调用存储过程先添加主表数据且
    返回ID到服务端,然后再根据返回的主表关联ID再添加从表的数据进数据库
      

  31.   

    如果主从表都是单一的数据,那么只需要调用存储就能实现了,
    如:
    CREATE  PROCEDURE aaa
    as
    declare @AutoID int
    inser into a(a)values('a')
    set @AutoID=SCOPE_IDENTITY()
    inser into b(AutoID,a)values(@AutoID,'b')
      

  32.   

    晕,还吵起来了啊?madyak(无天),别看你有两个星星,谦虚点好不,程序设计不是一成不变的,相同的功能实现方法很多,而这些实现方法的好坏标准也不一样,那要视功能细节要求而定,别总陷到自己的模式里。
      楼主的提问非常简捷“在三层中,自增字段(id),在新增完数据后,需要返回id,用delphi的数据感知控件,能实现吗?”,有些文学功底的人都能够看出来他的困惑是不知道如何取得返回ID,你在这里吵什么,你说的那些如果说有道理的话,也仅限在你的思维模式下罢了。
      你以为说一句“主从表”就表述很清楚了吗?给你举个例子吧,你的目的是从A城市到B城市,你跟我说说是坐“飞机、火车、汽车”这三种方式最先到达?AB距离、天气、路况、机场和车站远近等等你都不知道,你还吵着说坐XXX肯定快,简直笑话。
      

  33.   

    不要火药味那么大嘛认真看完了贴子内容后, 发现都是可以达成目标的, 不过偶本人是不支持Cassava(车超) 的作法, 过于浪费网络, 如果客户机在广域网上是非常慢的, 公文包模式现在基本上认为是必须的楼主不见了呵, 看看楼主怎么说
      

  34.   

    很久没这样争论过了。
        只是觉得在三层用自增ID麻烦,尤其是牵涉到主从表。如果再是个新手,弄不好做出来的东西是三层外表,两层的骨头。如果想做好,就必需有专门处理ID主从绑定的代码,这样一弄明显增加了工作量。对老手还不要紧,但对新手来说,绑定这部分很难,至少我不愿意这么干,除非在特殊环境下,不得已而为之。
      处理这些主从表同时增加,个为觉得核心代码和主要逻辑是如何绑定主从,且比较麻烦,尤其是一主对多从,或者主-半主-从串联起等。有人反对没什么,只是他们坚持自己的观点,但不拿出解决这些问题的办法。简单点就是有论点,没论据,吵了半天才说出来了一些。为了坚持用自增ID的可用性和不复杂性,却一字不提主从绑定的部分,这对新手来说是误导。
        问题焦点不是能不能解决,而是解决起来很麻烦,如果提前主从关联外键就是已知了,这样明显减小了不小的工作量。
      

  35.   

    madyak(无天) :1、“3150379,不知道你是谁的马甲”这是我唯一的号,请不要以小人之心度君子之腹。
    2、“信誉度怎么这么低?”信誉度低怎么了,你挂着两个星觉得好看是你的事,信誉度低的技术上就不如你了吗,可笑。
    3、“回答问题时,不见你的影子”一定要整天在这里回答问题的人才有权利在这里讨论吗?笑话。
    4、“有人回答了,你来说风凉话。”争开你的眼睛仔细看看,哪一句是风凉话,你回答问题,我也回答问题,不同意你的观点就叫风凉话吗?5、别总是说话盛气凌人的,你自己回头看看自己说的话,好象就你设计过三层结构,就你有经验,即使是这样,也应该谦虚一些吧,本人不才,N层结构也设计了一些,楼主这个问题我也曾经遇到过,但Cassava(车超)说的方法确实可行,我也没有说其他办法不行,只是说他的方法算是正解,你别总是网络流量网络流量的,就你知道广域网程序设计要考虑网络流量?别人都不知道?好象Cassava(车超)的方法就注定了要增加网络流量一样,可笑,只能说明你的设计思路太单一罢了。
      

  36.   

    请你不要误解,俺可没说你技术水平低,论坛里有很多高手是一个小角。
    说我盛气凌人,我不并觉得。只是我说自增ID的负面作用的几个问题。只是有人闭着眼说,自增ID好,有多么多么好,而不正面回答问题。如果一个系统以自增ID结构为主,只那个存储过程,是远远不够的,必需有一个系统的针对那些问题的解决方案。如果你坚持说这样很简单,没有增加工量作,我没话可说。也许你是高手吧。希望你能在论坛上多解决些问题,不建议所有象你一样的高手在这里潜水或者太谦虚了。也许你很少在网上发表些见解,这个贴中算是难得一见了,从你的一个角上就能看出来。希望在这个主题里没有影响到你。很久不用DELPHI了,但本人对DELPHI还是很有感情的,偶尔也来一下看看,增加些人气。
      

  37.   

    似乎这里好久没有这么热闹的帖子啦!凑个热闹我是今年年初写三层服务器程序的时候发现不能用自增字段的(sorry,好久没动手了,有些东西都忘得差不多了)。当时我的做法就是一般的处理:cds-datasetprovider-adoquery,设置了datasetprovider的属性popropogateChanges为true,目的是服务器能够在完成更新后可以把id回传,结果运行出错,提示autoinc字段不能这样处理。上BDN查找了一下,也就是发现了http://bdn.borland.com/article/20847这个帖子,好像还有一个吧,按照里面的介绍修改了provider.pas的代码,也不通过。估计这个是delphi的bug吧~所以在设计上也就没有使用autoinc字段了~至于大家这里争论的,我觉得有点抓不住关键吧~Cassava(车超)的回复是如何去获取一个可增的Id,但显然用存储过程获取的Id不是这个概念上的自增加id吧!自增加字段应该是新增记录保存后由数据库自行分配的一个值,这个值不用我们去关心。而我们用存储过程获得的id,则是我们可以使用规则定义并产生的一个字段值,所以我认为这里是两个不同的概念。按照楼主的提法,他应该是希望使用一个id由数据库产生并回传。根据我自己的实验记录,不应该直接使用autoinc字段,自己定义一个字段(int或varchar的都可以),通过后台存储过程计算这个字段的值并在中间层进行赋值处理就可以了。ddqqyy(ddqqyy)的回复里面给出了完整的思路,BDN的例子也是这样处理的。不过,ddqqyy(ddqqyy)的回复里最后用到了clientdataset.refresh,其实没有必要这样做。只要datasetprovider的属性popropogateChanges为true,后台所作的修改就会自动提交到客户端,注意,被提交数据中在中间层被修改了的才会回传,这样子网络的负荷会明显少很多。而refresh则是对clientdataset中的所有数据进行一次再查询,这个负荷会很大这是我对三层里面使用自增字段的一些看法,请各位指教指教
      

  38.   

    看了半天学习了很多,不过总结下来还是oracle简单,一个sequence解决这个问题,需要用自增用sequence产生,sequence的当前数可以查到。
      

  39.   

    但是我认为
    ********************************************
     回复人:Cassava(车超) ( 五级(中级)) 信誉:100  2006-11-7 18:22:57  得分:0
    ?  如果主从表都是单一的数据,那么只需要调用存储就能实现了,
    如:
    CREATE PROCEDURE aaa
    as
    declare @AutoID int
    inser into a(a)values('a')
    set @AutoID=SCOPE_IDENTITY()
    inser into b(AutoID,a)values(@AutoID,'b')************************************************
    这段代码是可取的,实际上是把数据库自动产生的ID让数据库自己去处理。并不需要回传到client端阿,这样的存储过程我也是写过的。不过我没做过三层,不敢说这个就一定适合三层。
      

  40.   

    呵呵,很久不用DELPHI了,生怕再忘光了。当年这可是吃饭的家伙。
      

  41.   

    强烈鄙视问题解决后不结贴的人!
    强烈鄙视技术问题解决后把贴子转移到非技术区的人!
    鄙视你们!http://community.csdn.net/Expert/topic/5216/5216675.xml?temp=.9262659