如何让系统自动生成入库单号(20分)

  • 主题发起人 主题发起人 mkbss
  • 开始时间 开始时间
我赞成网中戏的方法。我想你的商品入库应该是做从主从表关系的,如果是这样的话,对于入库单号可以这样处理:输入时产生一个临时入库单号,然后在触发器中按williamlea的方法修改入库单号。
 
基本的步骤是:
1、在事务外在业务表中查找一个最大号备用
2、开始一个事务,在事务中用行锁定的方式访问最大号表,查找最大号表中管理的最大号
如果没有记录则直接将当前最大号+1并插入最大号管理表,如果有则取出值如取出的值比事务外查到的值还小说明数据非法,此时以事务外取到的值为准+1,并将该值update到最大号管理表,否则以最大号管理表为准+1并update最大号管理表

参考代码如下 由于我的最大号管理表比较复杂所以删除了一部分相关处理 代码只能说明处理逻辑

function TDM_czxQueryBase.GetYWMaxIdByTxTTable(var sCount, sMaxId: string): string;
//本函数必须在事务中调用
var
sTemp,:string;
nCount:integer;
sSql:string;
begin
sTemp:=sMaxId;
result:=StrFieldAdd(nCount,sTemp);
if result<>'' then Exit;
sSql:='select MAXNO_VALUE from TXT_YWMAXNOGL with (updlock);
result:=ExeSql(Query_Exe,sSql,COpen);
if result<>'' then Exit;
if Query_Exe.Eof and Query_Exe.Bof then//如果表中没有记录则插入
begin
sSql:='insert into YWMAXNO with (updlock) (MAXNO) values (+#39+sYwTbMax+#39)';
result:=ExeSql(Query_Exe,sSql,CExec);
if result<>'' then Exit;
result:=StrFieldAdd(-nCount+1,sYwTbMax);
end
else begin
sTemp:=Query_Exe.FieldByName('MAXNO_VALUE').AsString;
result:=StrFieldAdd(nCount,sTemp);
if sTxtTbMax<sYwTbMax then//如果表中的最大号小于实际业务表的最大号 则以实际业务表为准
sTxtTbMax:=sYwTbMax;
if result<>'' then Exit;
sSql:='update TXT_YWMAXNOGL with (updlock) set MAXNO_VALUE='+#39+sTxtTbMax+#39
result:=ExeSql(Query_Exe,sSql,CExec);
if result<>'' then Exit;
result:=StrFieldAdd(-nCount+1,sTxtTbMax);
end;
end;

这里是获取最大号的部分
sSql:='select max('+sField+') aa from '+sTable+' where
result:=ExeSql(Query_Exe,sSql,COpen);
if result<>'' then Exit;
sMaxId:=Query_Exe.FieldByName('aa').AsString;
while true do
begin
StartTran(sTranName);
try
result:=GetYWMaxIdByTxtTable(sCount,sMaxYwTb,sMaxId);
if result='' then
begin
EndTran(sTranName);
Break;
end
else begin
RollBackTran(sTranName);
if Pos('Key violation',result)=0 then Exit;//如果不是主键重复则退出,否则重复插入主键。
end;
except
RollBackTran(sTranName);
end;
end;
sMaxId:=sMaxYwTb;
 
我给你提供一个最好的方法
用存储过程来实现,这样是不会重复的!在服务器端实现
create proc spBill(
@Bill varchar(32) output,
@dt datetime output
)
as
declare
@s varchar(2)
select @dt=getdate()
--年
select @Bill=cast(datepart(year,@dt) as varchar)
--月
if len(cast(datepart(month,@dt) as varchar))=1
select @Bill=@Bill+'0'+cast(datepart(month,@dt) as varchar)
else
select @Bill=@Bill+cast(datepart(month,@dt) as varchar)
--日
if len(cast(datepart(day,@dt) as varchar))=1
select @Bill=@Bill+'0'+cast(datepart(day,@dt) as varchar)
else
select @Bill=@Bill+cast(datepart(day,@dt) as varchar)
--时
if len(cast(datepart(hour,@dt) as varchar))=1
select @Bill=@Bill+'0'+cast(datepart(hour,@dt) as varchar)
else
select @Bill=@Bill+cast(datepart(hour,@dt) as varchar)
--分
if len(cast(datepart(minute,@dt) as varchar))=1
select @Bill=@Bill+'0'+cast(datepart(minute,@dt) as varchar)
else
select @Bill=@Bill+cast(datepart(minute,@dt) as varchar)
--秒
if len(cast(datepart(second,@dt) as varchar))=1
select @Bill=@Bill+'0'+cast(datepart(second,@dt) as varchar)
else
select @Bill=@Bill+cast(datepart(second,@dt) as varchar)
go
通过当前的时间来得到单据号码,精确到秒是不会重复的
 
如果同时调用存储过程会不会出现重号?难道存储过程会自己解决同时调用这个问题?
 
在实际应用中我也碰到这样的问题:
1、直接+1,多用户会冲突;
2、在触发器里面再来生成编号,那么在输入单据的时候,不知道这张单会落在哪里,管理起单据不方便。

个人认为2才是正道,但是就是没法在完成之前知道单号,只能存盘后再返回一个准确的单号;
再有一个问题,如果删除单后,会出现断号,用触发器判断空号可能有难度(这个问题比较小,可以判断得出来,这又涉及到触发器的效率问题)。

想想头都大了, 经常解决的方法象前面提到的那些,前面或者后面加客户端标志。


觉得最理想的方法就是:
1、每开始一个新单,就返回一个合理的单号,同时在服务器端标志该单号已被用;
2、存盘时按得到的单号直接存;
3、取消存盘时,服务器端要释放掉这个单号的占用,让下一个申请能得到这个单号;
4、能自动处理好空号问题,实际时3、点的扩充,释放的单号能及时分配出去。

实际上,这个方法也不是完美无瑕,因为,每天最后一张单,前面的还是会存在空号,空号的数量最多为总的客户端数量-1。
 
多客户单同时取号问题 我写的时候也碰到过
自己用了个笨办法 取得一个号后就往一个用来存放号的表里写条数据进去 表示被占用
取消存盘也不管 反正就一个号 有些客户不一定需要连续
 
jenhon我想您应该尝试在select max(id)到insert into ...
这一段如果用事务包起来就不会出现取出重复序号的问题了
但是这种事务模型因为是在事务中select max 会锁定太多记录,容易阻塞和互锁,相对不是一种好的办法
 
查詢最大號的時候在事務中下排它鎖,不過效率不好.
 
1.最大序号存在一张表里,在数据保存的时候才去生成单据号,生成单据号时用存储过程,
并且保存是采取缓存更新,万一重复,保存会失败,再重新保存一次就可以了
2.加客户端编号
不过第2种排序时不太方便,所以现在采用第一种
 
我写过这样的东东,呵呵,不过是HIS中生成处方号,道理应该是一样的。回头给你看看。
 
服务器端控制,谁先保存谁先占号,不应该有冲突呀?存储完成后再返回编号给客户端,应该没问题?
 
帮顶.............
 
保存的时候才生成新的单据号,当然由服务器控制,应该没什么问题吧
 
后退
顶部