用存储过程实现才是最保险的方法 如: CREATE PROCEDURE aaa @AutoID int OutPut as inser into a(a)values('a') set @AutoID=SCOPE_IDENTITY() //请看SCOPE_IDENTITY()的帮助就清楚了,它能解决多用户同时操作而造成不能取得最新的正确id的问题
自增长本来就是 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
CREATE PROCEDURE aaa @AutoID int OutPut as inser into a(a)values('a') set @AutoID=SCOPE_IDENTITY() //请看SCOPE_IDENTITY()的帮助就清楚了,它能解决多用户同时操作而造成不能取得最新的正确id的问题
服务端实现, 然后回送新的 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]
为什么不用自增, 你看了这么多相关后估计也知了,就是说用了自增就只能以 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;
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;这个是个函数入口得有点头晕,
如果主从表都是单一的数据,那么只需要调用存储就能实现了, 如: 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')
myid = table.FieldByName('id').AsInteger;数据感知控件,刷新一下状态,应该也能获取得到了
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,应该是高手的方法吧。
表不设主键=等死
字段设置默认值(包含自增字段)=找死 (3层用中使用ClientDataset时)
如:
CREATE PROCEDURE aaa
@AutoID int OutPut
as
inser into a(a)values('a')
set @AutoID=SCOPE_IDENTITY() //请看SCOPE_IDENTITY()的帮助就清楚了,它能解决多用户同时操作而造成不能取得最新的正确id的问题
参见http://community.csdn.net/Expert/topic/5070/5070916.xml?temp=.4269068
[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
这样的问题上面好象都没有解决吧。
虽然能解决,解决方法也不是一种,但这些都增大了网络的开销或增加了开发难度。但根源是一个自动增长带来了。
解决主从表的方法好象有点问题,BeforeUpdateRecord是在一个事务内部执行的,在此得不断的close,open好象不太合适。
CREATE PROCEDURE aaa
@AutoID int OutPut
as
inser into a(a)values('a')
set @AutoID=SCOPE_IDENTITY() //请看SCOPE_IDENTITY()的帮助就清楚了,它能解决多用户同时操作而造成不能取得最新的正确id的问题
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]
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;
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;这个是个函数入口得有点头晕,
Cassava(车超) 说主从表同时增加数据时很简单,严重不同意。
客户端不能每增加一条数据就取一次ID,这和两层没什么区别,如果实现公文包离线工作,这样设计就是找死。
UpdateRecord事件是在一个事务内部反复循环执行,在这里不能为了取ID,出现CLOSE,OPEN,以及执行SQL等语句。除非为这单独建立一个独立的数据连接,但这严重破坏了事物的作用,也大大的降低了性能,弄不好就执行不过去。总之这里不能随意操作数据库。如果提交前主从表的关联键就已确定,以上这些问题就全部不存在了。
用GUID代替自增ID在开发上要容易的多
2.考虑效率问题,不赞同用GUID号做位key,毕竟字符串的比较要花时间的.
3.保证唯一key,可以另建立一张表tbl_mainkey(key_name varchar(10), num int)
每次从num里拿号,然后把num+1,就不会重复了.
GUID也是可行的,但太长,没有办法的情况下可以考虑。
Cassava(车超) 说主从表同时增加数据时很简单,严重不同意。
客户端不能每增加一条数据就取一次ID,这和两层没什么区别,如果实现公文包离线工作,这样设计就是找死。
==========================================
这段话有问题,添加数据主要由中间层来处理,这也符合多层开发的数据逻辑和企业逻辑,而不是客户端直接让数据库执行它的SQL语句。
我觉得我的方法绝对简单,其实我觉得我已经写出了关键,也正确地回答了楼主的问题,
就算延伸到主从表,看了我的代码我想一般的程序员都应该知道怎么做了,madyak(无天)你毕竟是两个星星的的人物,难道就真的不会吗?或还是象前面一样装看不懂呢?如果是装不懂的,我再说破嘴也没用
代码我觉得没什么好贴的了,因为基本上不需要什么代码就基本上能实现了,相信madyak(无天)你是会了的,我也没什么好说的了
有些字段不设默认值才是找死;"用GUID代替自增ID在开发上要容易的多"
GUID会产生效率问题,而且数据库主健关联一旦复杂.这种方式显示非常恶劣;特别某些
要做ORM开发的,会被那个长长的唯一码搞死;
以下回答应该是比较佳的解法;
"
从头看到尾,终于完了,看累了,我就来总结一下吧!1.三层中别用AutoID, 这是硬道理.
2.考虑效率问题,不赞同用GUID号做位key,毕竟字符串的比较要花时间的.
3.保证唯一key,可以另建立一张表tbl_mainkey(key_name varchar(10), num int)
每次从num里拿号,然后把num+1,就不会重复了.
"
只是要补充一点每访问一次数据库可以取N个ID回来;
你是不是没做过三层呀?
觉得你没有客户端和服务端的概念
----------------------------------------
虽然水平没你高了,但请不要怀疑我没有做过三层啊返回客户端
先输完主从表数据,然后写一个主从表更新的接口方法把数据传到服务器,然后服务器先调用存储过程先添加主表数据且
返回ID到服务端,然后再根据返回的主表关联ID再添加从表的数据进数据库
如:
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),在新增完数据后,需要返回id,用delphi的数据感知控件,能实现吗?”,有些文学功底的人都能够看出来他的困惑是不知道如何取得返回ID,你在这里吵什么,你说的那些如果说有道理的话,也仅限在你的思维模式下罢了。
你以为说一句“主从表”就表述很清楚了吗?给你举个例子吧,你的目的是从A城市到B城市,你跟我说说是坐“飞机、火车、汽车”这三种方式最先到达?AB距离、天气、路况、机场和车站远近等等你都不知道,你还吵着说坐XXX肯定快,简直笑话。
只是觉得在三层用自增ID麻烦,尤其是牵涉到主从表。如果再是个新手,弄不好做出来的东西是三层外表,两层的骨头。如果想做好,就必需有专门处理ID主从绑定的代码,这样一弄明显增加了工作量。对老手还不要紧,但对新手来说,绑定这部分很难,至少我不愿意这么干,除非在特殊环境下,不得已而为之。
处理这些主从表同时增加,个为觉得核心代码和主要逻辑是如何绑定主从,且比较麻烦,尤其是一主对多从,或者主-半主-从串联起等。有人反对没什么,只是他们坚持自己的观点,但不拿出解决这些问题的办法。简单点就是有论点,没论据,吵了半天才说出来了一些。为了坚持用自增ID的可用性和不复杂性,却一字不提主从绑定的部分,这对新手来说是误导。
问题焦点不是能不能解决,而是解决起来很麻烦,如果提前主从关联外键就是已知了,这样明显减小了不小的工作量。
2、“信誉度怎么这么低?”信誉度低怎么了,你挂着两个星觉得好看是你的事,信誉度低的技术上就不如你了吗,可笑。
3、“回答问题时,不见你的影子”一定要整天在这里回答问题的人才有权利在这里讨论吗?笑话。
4、“有人回答了,你来说风凉话。”争开你的眼睛仔细看看,哪一句是风凉话,你回答问题,我也回答问题,不同意你的观点就叫风凉话吗?5、别总是说话盛气凌人的,你自己回头看看自己说的话,好象就你设计过三层结构,就你有经验,即使是这样,也应该谦虚一些吧,本人不才,N层结构也设计了一些,楼主这个问题我也曾经遇到过,但Cassava(车超)说的方法确实可行,我也没有说其他办法不行,只是说他的方法算是正解,你别总是网络流量网络流量的,就你知道广域网程序设计要考虑网络流量?别人都不知道?好象Cassava(车超)的方法就注定了要增加网络流量一样,可笑,只能说明你的设计思路太单一罢了。
说我盛气凌人,我不并觉得。只是我说自增ID的负面作用的几个问题。只是有人闭着眼说,自增ID好,有多么多么好,而不正面回答问题。如果一个系统以自增ID结构为主,只那个存储过程,是远远不够的,必需有一个系统的针对那些问题的解决方案。如果你坚持说这样很简单,没有增加工量作,我没话可说。也许你是高手吧。希望你能在论坛上多解决些问题,不建议所有象你一样的高手在这里潜水或者太谦虚了。也许你很少在网上发表些见解,这个贴中算是难得一见了,从你的一个角上就能看出来。希望在这个主题里没有影响到你。很久不用DELPHI了,但本人对DELPHI还是很有感情的,偶尔也来一下看看,增加些人气。
********************************************
回复人: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端阿,这样的存储过程我也是写过的。不过我没做过三层,不敢说这个就一定适合三层。
强烈鄙视技术问题解决后把贴子转移到非技术区的人!
鄙视你们!http://community.csdn.net/Expert/topic/5216/5216675.xml?temp=.9262659