这种情况不应该使用事务,加个时间戳即可。 修改楼主的代码如下:USE tempdb GO --建表 IF OBJECT_ID(N'[sys_billno]',N'U') IS NOT NULL DROP TABLE [dbo].[sys_billno] GO CREATE TABLE [dbo].[sys_billno]( [ID] [int] NOT NULL, [bill_type] [varchar](10) NULL, [sn] [int] NULL, [ts] TIMESTAMP, --加了个时间戳 CONSTRAINT [PK_sys_billno] PRIMARY KEY CLUSTERED ([ID] ASC) ) ON [PRIMARY] GO insert into sys_billno(ID,bill_type,sn) values(1,'Pos',1),(2,'INV',1); GO--建存储过程 IF OBJECT_ID(N'[dbo].[get_bill]',N'P') IS NOT NULL DROP PROC [dbo].[get_bill] GO create proc [dbo].[get_bill](@type varchar(10),@billno varchar(20) output) as begin declare @ts timestamp,@sn int select @sn=sn,@ts=ts from sys_billno where bill_type=@type update sys_billno set sn=sn+1 where bill_type=@type and ts=@ts
while @@ROWCOUNT=0 begin select @sn=sn,@ts=ts from sys_billno where bill_type=@type update sys_billno set sn=sn+1 where bill_type=@type and ts=@ts end
set @billno=@type+convert(varchar(10),@sn) end GO 存储过程的调用方法与楼主原来的相同。高并发环境下,也不会有任何问题。如OK,记得结贴哦!
用事务,先把流水号的字段相应加1,然后再生成单号,再添加数据到表单, 成功就提交事务,不成功就回滚。 SET ANSI_WARNINGS OFF set nocount on declare @intError int declare @billNo varchar(50) set @intError = 0 BEGIN TRAN t1 update s_seeks set seekvalue = seekvalue +1 where category = '%0:s' and datediff(day, seekdate, getdate()) = 0 and seektag = '%1:s'
SELECT @billNo = (select sTag from s_billcoderegu where billCode = '%0:s') + convert(varchar(6), seekdate, 12) + seektag + dbo.fcFormatInt('000', seekvalue-1) from s_seeks where category = '%0:s' and datediff(day, seekdate, getdate()) = 0 and seektag = '%1:s' --这里是添加相应的表单数据 set @intError = @@error if @intError = 0 commit tran t1 else rollback tran t1 SET ANSI_WARNINGS ON set nocount off
在默认的读已提交隔离级别中,共享锁 一 select 完就马上释放,而不是等到事务结束
反之,排它锁 一直持有到 事务结束。
在高并发的情况下保证得到的单号不重复 并且不死锁
1.我们这里几个系统 都是在事务中先 update 加1 然后 select (当然不调换位置也行,select 语句 加上 xlock,holdlock 也可),从来没出现过重复单号,也没有死锁过,是你哪里搞错了。
2.你这张表 最多不就几十行数据,锁住整张表代价一点都不大,如果你觉得这样的代价也大的话那么数据库中的几百张可能会 查询,插入,更新的 表,每张表每次 select 都要加 行锁,见过这样的 系统吗,从来没见过!
修改楼主的代码如下:USE tempdb
GO
--建表
IF OBJECT_ID(N'[sys_billno]',N'U') IS NOT NULL
DROP TABLE [dbo].[sys_billno]
GO
CREATE TABLE [dbo].[sys_billno](
[ID] [int] NOT NULL,
[bill_type] [varchar](10) NULL,
[sn] [int] NULL,
[ts] TIMESTAMP, --加了个时间戳
CONSTRAINT [PK_sys_billno] PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY]
GO
insert into sys_billno(ID,bill_type,sn) values(1,'Pos',1),(2,'INV',1);
GO--建存储过程
IF OBJECT_ID(N'[dbo].[get_bill]',N'P') IS NOT NULL
DROP PROC [dbo].[get_bill]
GO
create proc [dbo].[get_bill](@type varchar(10),@billno varchar(20) output)
as
begin
declare @ts timestamp,@sn int
select @sn=sn,@ts=ts from sys_billno where bill_type=@type
update sys_billno set sn=sn+1 where bill_type=@type and ts=@ts
while @@ROWCOUNT=0
begin
select @sn=sn,@ts=ts from sys_billno where bill_type=@type
update sys_billno set sn=sn+1 where bill_type=@type and ts=@ts
end
set @billno=@type+convert(varchar(10),@sn)
end
GO
存储过程的调用方法与楼主原来的相同。高并发环境下,也不会有任何问题。如OK,记得结贴哦!
看了一下锁的情况,不是行锁不起作用是select 的时候 会对每个行都请求行锁。
因为第一个事务,已经对某个资源 进行了加锁, 到第二个事务的select 无法请求到 inv那个锁了。
update 的是用的是U锁 就是S-》X的一个过程,也是一样的。因此在高并发的情况下 一定出现死锁。直接加tablock锁
不过2008R2的后续版本已经增加这个功能。
SET ANSI_WARNINGS OFF
set nocount on
declare @intError int
declare @billNo varchar(50)
set @intError = 0
BEGIN TRAN t1
update s_seeks set seekvalue = seekvalue +1
where category = '%0:s' and datediff(day, seekdate, getdate()) = 0 and seektag = '%1:s'
SELECT @billNo = (select sTag from s_billcoderegu where billCode = '%0:s') + convert(varchar(6), seekdate, 12) + seektag + dbo.fcFormatInt('000', seekvalue-1)
from s_seeks
where category = '%0:s' and datediff(day, seekdate, getdate()) = 0 and seektag = '%1:s' --这里是添加相应的表单数据
set @intError = @@error
if @intError = 0
commit tran t1
else
rollback tran t1
SET ANSI_WARNINGS ON
set nocount off