关于流水号问题的探讨
在业务系统的实现中,我们常常会遇到流水号的生成问题。举例来说,我手边有这样一个流水号:ALX-BF-201304-00201。其解释如下:(1)ALX——表示业务类型,安全阀离线报告
(2)BF——部门代码
(3)201304——年度和月度组合
(4)00201——5位流水号,以年为单位计算
我们把这个报告号连同报告的其他信息保存在一个数据表中。每当新生成一个报告(保存的时候),我们一般的做法是到数据库中即时读取现有的记录数量,然后+1,生成一个新的报告号。但是,这样做的问题在于,当客户填写报告的时候,并不知道这份报告的报告号应该是多少。也就是说,在客户保存这份报告之前,为了防止并发操作,我们并不会给这个报告一个报告号并告知客户。只有在数据保存时,系统才会计算并赋予最后的值。
为了更好的客户体验,我希望当客户进入报告填写的页面的时候,就明确的告诉客户当前所填写的报告的报告号是多少。我设计的如下的解决方案:
(1)我建立了一个流水号表,名字叫做tab_WaterNumber。包括以下字段:
WaterNumber【流水号,文本型】 DateTs【时间戳,时间型】 State【号码状态,整形】
State表示这个流水号的状态,1表示被占用中,2表示已经被使用,3表示被释放,可以再次复用。DateTs表示对这个号码最后的操作时间。
(2)在报告填写页面,页面初始化,我就检索tab_WaterNumber表,生成一个新的流水号并显示在页面上。同时,将这个流水号保存在tab_WaterNumber中,将DateTs置为生成号码的时间,将State置为1,表示这个号码被占用中。
(3)在报告填写页面中,我写了一个脚本,每60秒更新一次号码的DateTs,也就是续租一下。
(4)如果这个报告被保存,我就把这个报告流水号以及相应的报告信息写入到相应的业务表中。同时,更新tab_WaterNumber表。将这个号码的State置为2,表示这个号码被使用掉了。
(5)如果客户在操作中,因为种种原因,并没有保存这个报告。或者放弃,或者关闭浏览器,那么在tab_WaterNumber中的记录,State一直是1,DateTs是最后一次刷新时间。
(6)我在数据库中写了一个作业,每天晚上18:00执行(已经下班一个小时了)。检查tab_WaterNumber中,所有State是1,并且DateTs距离18:00超过1分钟的数据,将其State置为3,即把号源释放出来。
(7)也就是上述的第二步,当然,我在申请号源的时候,会首先检查是否有可以复用的号源。如果有就分配出来,将状态重新置为1。如果没有就按照算法新生成一个号码给客户。
那么,我这里有两个问题请教大家:
(1)大家是否有更好的解决方案来解决上面的问题;
(2)请大家帮我检查一下上面的方案是否存在逻辑错误?因为我在实际应用中,还是有比较大的几率发现,已经被分配并保存的号码,重新被分配出来了。我也不知道怎么回事。因为客户和我反馈说,有些报告号被分配过了,也已经被出具了。怎么又被分配出来了,我检查了代码,似乎没有发现问题,这让我很无奈!
谢谢大家,请不吝赐教! 流水号 算法 --------------------编程问答-------------------- 那就保存时生成,或者维护一个最大id的表,新建时,这个表中的值就加一了,后面不管谁再创建,都是顺序加一,不会重复 --------------------编程问答--------------------
我还有功能是复用释放的号源,所以,如果不考虑复用的话,您这样应该可以 --------------------编程问答--------------------
+1,我用的是维护最大表。预估了一个最大的级别(比如1个亿),然后批量生成了下id,每天上午2点的时候做一下数据的更新和同步,妥妥的。现实当大中并导致锁或冲突我真的见的少,照理好几个医院每天的各种票据还是蛮大的 --------------------编程问答-------------------- 第二个问题和第一个问题那就矛盾了
如果解决第一个问题 第二个问题就不存在
既然是流水号 所有的ID 都是唯一的 怎么可能存在相同的 或者复用的
在做作业的时候 是否把数据倒回了 或者该删的没删 检查下数据完整性 --------------------编程问答--------------------
直接在插入数据库时,让数据库去做这件事情.
然后把流水号返易做图来不就ok了吗? --------------------编程问答-------------------- select 'BX'+convert(varchar(4),datepart(yyyy,getdate()))+right('00000'+convert(varchar,isnull(Max(convert(int,right(流水号NO,4))),0)+1),6)
from table_Name where right(left(流水号NO,6),4)=datepart(yyyy,getdate())
仅供参考 --------------------编程问答-------------------- 填的时候先生成好 填完再插入。。 --------------------编程问答--------------------
是的,理论上应该不会发生已经分配的号码被再次分配的问题,我也检查了我的逻辑和代码,似乎没有发现问题。但是确实,现实情况下发生了这样的情况。所以我写出来请教大家,从我的描述中是否觉得我的逻辑存在问题! --------------------编程问答--------------------
这样做存在一个问题,比方说客户(客户A)在进入报告填写的页面,我就到数据库中计算流水号。假设流水号码是100号。此事,现在客户没有保存,我这个计算出来的号码也不会存进去。在这个时候,其他的客户端(客户B)也在操作这个页面,也是执行的这个操作,客户B也得到了流水号100。问题是,虽然客户A早进入页面,但是填写操作花了10分钟。而客户B虽然比A晚进入1分钟,但是填写页面只花了5分钟时间。这样就带来问题了,两个人保存的数据都是流水号100。存在数据重复了!
当然,我们可以在保存时重新计算一下新的流水号。通常的业务系统来说,基本不存在两个客户同时提交数据库保存的情况,这种并发性可以忽略不计了。但是如果重新计算的话,肯定是,客户A或者客户B,有一个人看到的流水号和真正数据保存的流水号不一致。客户体验就不好! --------------------编程问答-------------------- 帮顶! --------------------编程问答--------------------
不是很理解您的意思!假设以医院为例,现在医院每层都有收费窗口,就以收费单流水号为例。您可以每天固定时间批量生成一批流水号,假设5000个。那么,当上班以后,各个收费窗口如何去取得这些号源呢? --------------------编程问答-------------------- 综上所属,并查看了楼主您想出来的解决方案,可是如果换做是我的话,我会这样解决:首先假如上面的流水号是您所说的报告号的话,那么则就可以这样来,“①根据业务类型、部门编码、年月度组合、自动生成流水号,当客户在添加报告时,上述的编码及编号都其实已经是可以生成了,只需将其插入到数据库中”,而楼主所说的并发操作,我认为当插入时已经可以计算出报告号,就可以将报告号和报告一起发回给客户。但是假如此报告号已经存在了楼主所说的被占用、或者已经被使用等,则要通过回滚操作来控制此报告的生成和此报告号的发送、生成。
补充:.NET技术 , ASP.NET