当前位置: 代码迷 >> Sql Server >> 关于在SQL Server中生成流水号的步骤的讨论
  详细解决方案

关于在SQL Server中生成流水号的步骤的讨论

热度:39   发布时间:2016-04-24 18:15:19.0
关于在SQL Server中生成流水号的方法的讨论





begin tran
select top 1 * from dbo.t with(readpast) where id>10 


commit


--关于在SQL Server中生成流水号的方法的讨论

--第一种方法
--有些是用种子表, 
--在种子表中记录流水号,产生新的流水号后更新种子表的"种子数据",
--也见过比较低级的,种子表的值用自增列实现,每次插入数据,取种子表的自增列的值,
--一开始还不理解,觉得没必要,后来发现他们是为了防止并发,产生重复的流水号
--后来在想,防止并发产生重复的流水号,必须用种子表吗?

--第二种方法
--有些是用函数,因为流水号有一定的组合规律,放在函数中,结合业务表的数据,组合生成,

--先不说第第二种做法,对于第一种做法,
--原因无外乎是放在一个事物中,防止因为并发产生相同的流水号
--那么,原因知道了,有必要为了产生流水号,去增加一个表?
--怪不得有些系统,看起来表挺多的,一看,尼玛冗余一大堆

--这里实现不用种子表,生成不重复序号的方法,
--重复的原因就是因为在"同一时刻"没有把"资源锁定"
--那么,为了我们可不可以直接锁定业务表呢?


--随便创建一张表,当做是业务表,这里为了简化,流水号为整数类型,主要为了说明实现方法
--试一下updlock吧,在查询max(id)时候,锁定表,在当前事务提交之前,其他事物无法获取max(id)的值
--测试一下

drop table t

create table t
(
id int,
remark varchar(50)
--其他字段不做一一列举,
)

insert into t values (1,'A');

dbcc tran

begin tran
declare @i int
select @i=max(id) from t with(UPDLOCK);
--种子生成了
--下面该具体的业务操作了
--暂时不提交这个事务,其他链接无法获取max(id)
commit;


--另外开一个窗口,select max(id) 是要被阻塞的
--除非当前事务提交



--有人会质疑select max(id)的效率,
--其实ID列上加上索引的话,select max(id)的效率完全不用担心
--这样,既可以生成不重复的流水号,又省下了一张表,可以将精力集中于业务逻辑
--避免建立一些蛋疼的表

--另外,也见过不少人,照猫画虎,人家用种子表生成流水号,我也这样用
--可惜生成种子的时候,连事物都不开,这种方式,连种子表的本意都给误会了





当然种子表也不是瞎用的,用不好照样会出问题,参考大神的博客
http://www.cnblogs.com/studyzy/archive/2008/11/28/1342950.html


分享最近另外一个小知识点:不禁感慨,高人真多
http://www.windbi.com/showtopic-120254.aspx

下班了,下班了,赶紧闪!!!
------解决方案--------------------
关键是这会就下班了 羡慕。
------解决方案--------------------
下班了,先收藏下周一在看。
------解决方案--------------------
等下班。。。 
------解决方案--------------------
谢谢分享。

之前论坛里,也有网友问类似的问题,这个updlock,确实是防止select到重复的最大值,很有帮助,就是使查询数据的时候,也会锁住数据
------解决方案--------------------
LZ下班真早啊
------解决方案--------------------


如果整个项目都有用一个临时表来记录这个ID那?
------解决方案--------------------
这个跟业务联系太大了,貌似没有绝对的方案吧
------解决方案--------------------
update tbAllId
set curid=curid+1
output curid
where app=123

------解决方案--------------------
两种方法都可以了,只是第一种方法只用一张表就行了(另加一"类型"字段以区分不同的表)
我习惯用第二种方法
------解决方案--------------------
感谢分享.

------解决方案--------------------
引用:
update tbAllId
set curid=curid+1
output inserted.curid
where app=123


谁能验证一下它的并发性?
下面是我的验证方式,需要开多个查询窗口一起执行:

declare @i int,@v varchar(50)
declare @t table(fmax int,flast datetime)
declare @td varchar(50)


declare @s datetime
set @s=getdate()
set @v=convert(varchar(50),@s,120)
print @v
set @v=left(@v,18)+'9'
set @s=@v
while @s<getdate()
set @s=dateadd(ss,10,@s)
print convert(varchar(50),@s,120)


waitfor time @s --确保多个查询窗口在 同时 开始执行

set @v='tba'
set @i=1
while @i<300
begin

set @td='0:00:00.00'+cast(cast(rand()*9+1 as int)  as varchar(1))
--print @td
WAITFOR DELAY @td --随机延时几个毫秒

--select * from tballmax where fname=@v
----select @d=fmax from (
update tbAllmax
set fmax=fmax+1,flast=getdate()
output INSERTED.fmax,inserted.flast into @t
where fname=@v
----) a
----print @d



--select * from tballmax where fname=@v

set @i=@i+1
end

select * from @t


另外,系统自身的自动递增的identity字段,应该也不会有并发问题吧
------解决方案--------------------
以前保险的方法是with(paglock,xlock),现在SQL2012及之后都用 SEQUENCE .

http://technet.microsoft.com/zh-cn/library/ff878091.aspx
  相关解决方案