在C/S模式下:
我做企业单据录入的摸块时, 其中有个"单据号"的字段是我软件里面自动生成的,
我的方法是这样的:
1,从数据库该表里面取"单据号"最大的一条记录,然后将他的"单据号"加上1,作为
新的单据.(也就是用户在点"新建"按钮的时候,我自动生成一个新的单据号).这种方式如果多个用户使用的时候会出现错误,
比如:两个用户同时操作的时候,他们获得的单据号都是一样的,
谁先保存单据的人能成功,后者就会出现关键字重复了,
因为这张单据现在已经存在了!弟兄们,有什么好的办法解决啊?我用的是DELPHI6+SERVER7.0,
我听说这种可以在SQLSERVER端控制的,
也搞不清楚了,大家帮忙啊!!!

解决方案 »

  1.   

    分都给我吧,我的办法完美无缺,彻底解决此类问题!将你的表的主键定义为uniqueidentifier类型的,然后可以在客户端产生一个GUID,或者在往表里新增数据的时候在SQL句子里用NewID()得到新的GUID。
    我现在就是在客户端用CreateGuid来得到数据库表中的新增记录的主键值,巨好使!其实你说的问题也困扰我很长时间的,呵呵。
      

  2.   

    我正好有一个这样的函数,不过是在ORACLE上的,给你看看,不知道是不是对你有用?
    (*******************************************************************************
    *   函数名:GetAutoCode
    *
    *   函数说明:    根据数据库中的表内容,取得一个比原来记录最大的编号+1的值
    *
    *   参数说明:
    *   ADOConnection1  :对数据进行查询的数据库连接
    *   TableName       :需要读取的表的名称
    *   Column          :需要读取的字段的名称
    *   CurrDate        :编号左边的固定值,取的是服务器的当前日期
    *   CodeLen         :编号长度
    *******************************************************************************)
    function GetAutoCode(ADOConnection1 : TADOConnection; Table_str : string; Column_str : string ; CurrDate_str : String; CodeLen : integer) : string;
    var
        tmpNum,i : integer;
        MaxBillNo, tmpMax : String;
        tmp_format : array[0..20] of char;
        ADOdatasettmp : TADODataSet;
    begin
        ADOdatasettmp := TADODataSet.Create(nil);
        ADOdatasettmp.Connection := ADOConnection1;
        FillChar(tmp_format, CodeLen, '0');
        ADOdatasettmp.CommandText := 'select max(' + Column_str + ') MaxBillNO from ' + Table_str + ' where ' + Column_str + ' like''' + CurrDate_str + '%''' ;
        ADOdatasettmp.Open;
        MaxBillNo := ADOdatasettmp.FieldByName('MaxBillNO').AsString;
        if MaxBillNo<>'' then
        begin
            tmpMax := copy(MaxBillNo,9,length(MaxBillNo) - 8);
            tmpNum := StrToInt(tmpMax) + 1;
            tmpMax := InttoStr(tmpNum);
        end
        else
            tmpMax := '1';
        tmpMax := copy(tmp_format,1,CodeLen - 8 - length(tmpMax)) + tmpMax;
        Result := CurrDate_str + tmpMax;
    end;
      

  3.   

    to Randy_Mic(生于70年代)大侠
    我生成的单据号的格式如下;200212200001前四位:前八位刚好是日期:2002年12月20号,
    后面五位是序号,
    单据号的字段类型是"char"的,
    我不知道你方法是写代码实现的!
      

  4.   

    楼主:以上各位的方法都很好,不过,按你自己的办法也可行的:
    将单据编码在中间服务器中生成,响应DataProvider.BeforeUpdateRecord事件。下面是我以前程序中的代码,和你的情况差不多,不过用了两个字段做主键,共参考(Delphi5的,高版本事件的参数有些不一样):
    procedure TZfInfoServer.NewProviderBeforeUpdateRecord(Sender: TObject;
      SourceDS: TDataSet; DeltaDS: TClientDataSet; UpdateKind: TUpdateKind;
      var Applied: Boolean);
    begin
      // 如果客户程序插入信息记录同时未设定主键,
      // 从主窗口获取唯一的记录主键(发送时间和当日记录序号)
      if (UpdateKind = ukInsert) and (DeltaDS.FieldByName('NUM').Asinteger <= 0)
      begin
        DeltaDS.Edit;
        DeltaDS.FieldByName('NUM').AsInteger := ServerMainForm.RecNumber;
        DeltaDS.FieldByName('SENDDATE').AsDateTime := ServerMainForm.SendDate;
      end;
    end;
      

  5.   

    to  Drate(鸟窝里的虫) 
    谢谢你提供的涵数,
    生成单据号的代码我自己写了一个,不过没你的通用简洁
    不过你这里还是解决不了我的问题的,
    因为比如两个用户,照你的生成单据号的方法,他们申请到的单据号应该是一样的,
      

  6.   

    Oracle 里干吗还要写函数,放着很好的Sequence干吗不用?我觉得楼主的问题归根结底就是:你的单据号一定要在服务器端生成并加以保护,如果在客户端通过程序生成那么就会出现重复的记录
      

  7.   

    bluemeteor(挂月) (说的对的我想我是不是能在保存记录的时候在重新检测一次数据库,
    如果已经存在就重新生成一个单据号,如果没有的话就最好!!
    各位觉的这方法可行吗?怎么样啊?