于小澜, Another_eYes:
IDBuf是数据库中的一个实际表,字段有"表名"和"ID"两个,其中表名是主键,存放需
要生成标识的表的名称,ID存放这个表当前最大的ID值(因为一个表只有一个ID字段,所
以我没有设“ID字段名”字段)。
当要给人员基本信息增加一条记录时,用以下算法获取ID值:
select ID from IDBuf where 表名='Person' for update (这里已加锁)
DataID:=Query.ID;
DataID:=DataID+1;
update IDBuf set ID=DataID for 表名='Person'
commit (这里解锁)
至此,DataID就是新增记录的ID,插入记录时使用这个值就可以了。
从这个算法来看,Another_eYes所说的问题并不存在。但实际的程序要复杂得多,
当IDBuf中没有Person这个表的记录时,我就从Person表中取最大的ID,这可能会有一点
问题,这就是前一贴我说的“基本上”的意思。不过,发生冲突的可能性非常小,因为
只有当多人同时创建第一个相同的ID时才有可能发生冲突,而我的实际应用中根本就没
有发生过。
下面我把实际代码贴出来供参考。也请各位帮我看看能否把“基本上”这几个字去掉
(不要让我初始化IDBuf)。
function GetIDFromBuffer(DSetID:string):string;
begin
Result:='';
if TempQuery.ExecShortSQL(Format('select %s from %s Where %s=''%s'' for update',
[fnIDBufDataID,GetTableName(dsidIDBuf),fnIDBufDSetID,DSetID]),sstSelect)
and not TempQuery.IsEmpty then
Result:=TempQuery.Fields[0].AsString;
end;
//TempQuery.ExecShortSQL就是执行SQL语句,'for update'是Oracle的附带加锁的语法
//fnIDBufDataID等是一些常量和函数等
procedure PutIDIntoBuffer(DSetID,RecID:string);
begin
TempQuery.ExecShortSQL(Format('Insert into %s Values(''%s'',''%s'')',
[GetTableName(dsidIDBuf),DSetID,RecID]),sstNotSelect);
TempQuery.Database.DoCommit; //DoCommit是不怕嵌套的 StartTransaction和Commit组合
GetIDFromBuffer(DSetID); //锁定
end;
procedure ChangeBufferID(DSetID,RecID:string);
begin
TempQuery.ExecShortSQL(Format('Update %s Set %s=''%s'' where %s=''%s''',
[GetTableName(dsidIDBuf),fnIDBufDataID,RecID,fnIDBufDSetID,DSetID]),sstNotSelect);
TempQuery.Database.DoCommit; //提交并解锁
end;
function CreateDataID(DSetID,DFieldID:string; Reusable:boolean):string; //生成标识,不要理会Reusable
function GetFieldMax:string; //从目标表选取最大的ID,可能发生冲突的地方
begin
Result:='';
if TempQuery.ExecShortSQL(Format('select Max(%s) from %s',[DFieldID,GetTableName(DSetID)]),sstSelect)
and not TempQuery.IsEmpty then
Result:=TempQuery.Fields[0].AsString;
end;
function GetMaxLenOf(FieldName:string):integer;
begin
Result:=DefaultIDLength;
if DDDM.LocateDSet(NewID(DSetID)) and DDDM.LocateDField(DFieldID) then
Result:=DDDM.FldLen;
end;
var
MaxLength:Integer;
BufID:string;
begin
Result:='';
if Reusable then
Result:=GetFreeDataID(DSetID); //不要理会,Result==''
if Result='' then
begin
MaxLength:=GetMaxLenOf(DFieldID); //标识串的最大长度
Result:=GetIDFromBuffer(DSetID); //从IDBuf取ID
if Result='' then //只有第一次返回值才为空
begin
Result:=GetFieldMax; //从目标表选取最大的ID,可能发生冲突的地方
if Result='' then Result:='A';
PutIDIntoBuffer(DSetID,Result); //插入IDBuf
end;
if Length(Result)<MaxLength then
Result:=Result+CharS('A',MaxLength-Length(Result))
else
ChangeCodeToNext(Result,Length(Result));
ChangeBufferID(DSetID,Result); //修改并解锁
end;
end;
注:上面这些代码中涉及到的许多元素都是我自产的,所以,直接编译的话会有许多错误。