(求助)并发造成单据重复如何解决?

herong888 2013-12-24 05:29:31
生产环境是这样的:
t_BillCodeRule表存单据类型及各单据类型(如出库单)的最大单据编号的数字部分,比如00235
ICBillNo表存各单据类型最大单据的完整编号,比如TX00235


生成编号时是先从t_BillCodeRule表取出库单类型的最大编号。并储存在临时字段中,值是TX00235
再将00235+1的值00236写回t_BillCodeRule表
再将TX00236写回ICBillNo表
再将包括TX00235在内的信息返回供其他存储过程使用。

语句是:
CREATE procedure [dbo].[GetBillNo]
@Billtypeid int,
@Projectid int,
@FBillno nvarchar(20) OUTPUT
as
BEGIN TRANSACTION
Begin
Declare @TmpID nvarchar(10)
Declare @billno nvarchar(10)
Declare @qianz nvarchar(10)


SET @TmpID = (SELECT FID FROM t_BillCodeRule WITH(READUNCOMMITTED)
WHERE fbilltypeid=@Billtypeid and fprojectid=@Projectid)

select @qianz=Fprojectval FROM t_BillCodeRule WITH(READUNCOMMITTED)
WHERE fbilltypeid=@Billtypeid and fprojectid=1

update t_billcoderule with(rowlock) set fprojectval = fprojectval+1,
flength=case when (flength-len(fprojectval)) >= 0 then flength else len(fprojectval) end where FID = @TmpID

Update ICBillNo with(rowlock) Set FCurNo = (select top 1 isnull(fprojectval,1)
from t_billcoderule where FID = @TmpID) where fbillid = @Billtypeid
select @billno=left('0000000000',Flength-len(Fprojectval))+Fprojectval from t_billcoderule where FID = @TmpID

Update ICBillNo with(rowlock) Set FDesc = @billno Where FBillID = @Billtypeid
select @FBillno=@qianz+@billno

End
commit TRANSACTION
...全文
898 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
Yole 2013-12-25
  • 打赏
  • 举报
回复
引用 9 楼 herong888 的回复:
这破逻辑不是我干的,是金蝶软件的工程师的架构设计就这样 因为他在一张表中有N多种单据类型,不同的类型有不同的编号,如:xout0000136,qin002635,SEOUT0012364
取单据号的时候是要把这个最大单据编号的表锁上的,上面的存储过程本身应该没什么问题,问题在于用到这个存储过程的地方,是不是放在一个事物中进行处理的。 我以前用过用友的库,也是这样处理的!
herong888 2013-12-25
  • 打赏
  • 举报
回复
这破逻辑不是我干的,是金蝶软件的工程师的架构设计就这样 因为他在一张表中有N多种单据类型,不同的类型有不同的编号,如:xout0000136,qin002635,SEOUT0012364
marongc 2013-12-25
  • 打赏
  • 举报
回复
这个非得用SQL来解决吗,可以用开发工具来解决吗?
山寨DBA 2013-12-25
  • 打赏
  • 举报
回复
简历楼主恶补一下阻塞与死锁的知识
發糞塗牆 2013-12-25
  • 打赏
  • 举报
回复
引用 14 楼 herong888 的回复:
加了表锁和独占锁之后不知道会不会引起死锁
如果逻辑没问题就不怎么会,不然的话,死锁风险有增无减
herong888 2013-12-25
  • 打赏
  • 举报
回复
加了表锁和独占锁之后不知道会不会引起死锁
LongRui888 2013-12-25
  • 打赏
  • 举报
回复
引用 11 楼 herong888 的回复:
我也是这么想的,就是在查询第一张表的时候把该表锁上。等更新完之后再释放,问题是要怎么实现呢?请问 [quote=引用 10 楼 u010192842 的回复:] [quote=引用 9 楼 herong888 的回复:] 这破逻辑不是我干的,是金蝶软件的工程师的架构设计就这样 因为他在一张表中有N多种单据类型,不同的类型有不同的编号,如:xout0000136,qin002635,SEOUT0012364
取单据号的时候是要把这个最大单据编号的表锁上的,上面的存储过程本身应该没什么问题,问题在于用到这个存储过程的地方,是不是放在一个事物中进行处理的。 我以前用过用友的库,也是这样处理的![/quote][/quote] 试试这个办法,分别加了表锁,和独占锁:
select *
from 表 with(tablock,xlock)
發糞塗牆 2013-12-25
  • 打赏
  • 举报
回复
2005的话可以使用版本存储隔离级别来避免脏读
herong888 2013-12-25
  • 打赏
  • 举报
回复
我也是这么想的,就是在查询第一张表的时候把该表锁上。等更新完之后再释放,问题是要怎么实现呢?请问
引用 10 楼 u010192842 的回复:
[quote=引用 9 楼 herong888 的回复:] 这破逻辑不是我干的,是金蝶软件的工程师的架构设计就这样 因为他在一张表中有N多种单据类型,不同的类型有不同的编号,如:xout0000136,qin002635,SEOUT0012364
取单据号的时候是要把这个最大单据编号的表锁上的,上面的存储过程本身应该没什么问题,问题在于用到这个存储过程的地方,是不是放在一个事物中进行处理的。 我以前用过用友的库,也是这样处理的![/quote]
Andy__Huang 2013-12-24
  • 打赏
  • 举报
回复
还要注意的是新增插入数据时,要用事务处理.上面你已经用了事务,这里我再提醒一下.
Andy__Huang 2013-12-24
  • 打赏
  • 举报
回复
参考:
DECLARE @i int,@OrderNo nvarchar(20),@newOrderID int,@newOrderPaymentID int;
SET @OrderNo='BX'+replace(CONVERT(varchar(7),@OrderDate,120),'-','')
IF exists(SELECT 1 FROM RoomBookingOrder WHERE OrderNo like '%'+@OrderNo+'%')
BEGIN
    SELECT @i=cast(MAX(right(OrderNo,3)) as int)+1 FROM RoomBookingOrder WHERE OrderNo like '%'+@OrderNo+'%'
    SET @OrderNo=@OrderNo+'-'+STUFF('000', 4-len(@i), len(@i), @i)
END
ELSE
    SET @OrderNo=@OrderNo+'-001'

SELECT @OrderNo

Andy__Huang 2013-12-24
  • 打赏
  • 举报
回复
这不就是一个单据的流水编号问题吗? 没有那么复杂,感觉楼主说了这么多,你已经把自己的绕晕了,问题什么能够解决呢? 最简单的用事务处理,准备插入数据时生成编号,以最大ID号加上1为新的流水号,再加上单据前缀就是最新的单据编号,如:数据库最大单编号BX000236,那么插入数据时最新单据编号就是BX000237 还有,修改时单据编号不用修改.
华夏小卒 2013-12-24
  • 打赏
  • 举报
回复
对于你这个调用很频繁的问题,也不能直接把with 选项去掉,去掉,会挂起很多其他的事务进程。 所以需要从长记议,可能要改变思路,逻辑
华夏小卒 2013-12-24
  • 打赏
  • 举报
回复
WITH(READUNCOMMITTED) 有了这个,意味着,可以读取未提交的数据。 这个事导致出现重复的根源。
熊猫王子 2013-12-24
  • 打赏
  • 举报
回复
把下面这段业务放在一个事务中先行提交: 生成编号时是先从t_BillCodeRule表取出库单类型的最大编号。并储存在临时字段中,值是TX00235 再将00235+1的值00236写回t_BillCodeRule表 你现在的事务包含内容太多。
發糞塗牆 2013-12-24
  • 打赏
  • 举报
回复
早上有个人给了个代码我看,跟你这个有点类似,他说频繁死锁,我叫他去掉with,就没事了,我觉得你这个也可能会引起死锁
LongRui888 2013-12-24
  • 打赏
  • 举报
回复
这个: SET @TmpID = (SELECT FID FROM t_BillCodeRule WITH(READUNCOMMITTED) WHERE fbilltypeid=@Billtypeid and fprojectid=@Projectid) select @qianz=Fprojectval FROM t_BillCodeRule WITH(READUNCOMMITTED) WHERE fbilltypeid=@Billtypeid and fprojectid=1 用read uncommitted应该是由问题的把,读的是脏数据。

34,594

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server相关内容讨论专区
社区管理员
  • 基础类社区
  • 二月十六
  • 卖水果的net
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧