几台机器同时向数据库中追加记录。怎么避免某个字段发生重复

时间:2021-10-07 15:04:44
几台机器同时向SQL server数据库中追加记录。怎么避免某个字段发生重复?
这个编号是按照一个命名规则算出来的。比如都是BH开头的7个字符后5位为不重复的记录号码?

原来用Insert Into的SQL 语句追加的,先算出来不存在的编号再插入。很显然几台机器同时操作时会有重复的编号而产生错误(是不允许重复的索引字段)。这样的问题怎样做比较好呢?
用存储过程可以解决吗?怎么写呢?或者有什么更好的办法吗?
比如字段如下:
BHID (编号,varchar类型)(BH00001,BH00002......)
txt1 (varchar 类型)
txt2 (char 类型)
num (float 类型)
请高手给出部分代码。谢谢

19 个解决方案

#1


不是很明白你的意思
我觉得你可以建一个表
ID     flag
00001  0
00002  0
......


然后都从这个表中取ID,更新flag=1,不知能不能解决你的问题。

#2


将编号字段设置成主键或唯一键.

插入的时候,如果出现冲突,就再重新取编号,再插入就可以了.

这样的效率高于用锁. 因为用锁要锁住整个表,直到插入操作完成. 这样,即使正常的读数据也会受到影响到.

#3


这一点sybase的处理比sql server好,因为sybase可以支持表锁、页锁、行锁等多种方式。

#4


设置主键。

#5


可設一自動增加的id 做為主健,在增加記錄時先增加id 然后讀出id 再更新其它字段

#6


to: ropriest(馆主) 

你错了,SQL一样有锁,不过用锁的效率很低而已.(sybase的道理也一样)
因为你要保证取得的编号不重复的话
那么你必须在取编号时,锁住整个表,不然你无法保证编号的唯一
而锁住整个表很显然不利于多用户的并发操作.


#7


举个例子来说

不锁表的情况:
假设你要插入一条记录,那么你就取最大编号:不是N+1,然后进行插入
如果在你取编号后,插入记录前,又有另一个用户要插入记录,那很显然,因为你的记录还未插
入,那另一个用户取得的编号仍然是N+1,和你的一样,这样就导致了编号重复.


只锁最大记录的情况:
假设你要插入一条记录,那么你就取最大编号:不是N+1,然后进行插入
如果在你取编号后,插入记录前,又有另一个用户修改记录,他将某条记录修改成了N+!
那结果和上面一样,导致了编号重复.


锁整个表的情况:
因为你在取编号时,锁住了整个表,那很显然,就相当于你独占了整个表,此时,可以避免编号重复,但另一个后果是,所有的用户都必须等你释放了锁后,才能使用,包括只是读取表的用户,如果迸发用户多的话,很显然严重影响效率.

#8


我觉得yosonlin() 的方法可能比较方便,但是好像不太适合我的用处了。我的数据库中已经有了很多数据了,并且这样修改对我原来的程序可能影响比较大。
zjcxc(邹建) 的办法应该也比较好,但不知这个判断冲突和重新编号的过程应该放在那里做比较好呢?在我的程序中判断还是写到存储过程中呢?
我现有的系统是从其他系统中导过来的数据,并没有实现追加记录的功能。现在我要添加追加记录的功能,所以碰到了这个问题。现有的数据库中没有任何存储过程。

#9


zjcxc(邹建) 说的很对。我就是不明白怎么作才好了。

#10


一个简单的办法,用存储过程做,把算号和插入,放在一个事务里
选号不要使用with (no lock)
例子:
--create table t1( i int )
begin tran
declare @a int
select @a = isnull( max(i),0 )+1 from t1 

insert into t1 (i) values ( @a )

commit tran

#11


to zjcxc(邹建) :如果我把插入记录写成一个存储过程。如果多个用户同时调用这个存储过程,会不会也出现这中重复记录的事情呢?

存储过程在多个用户调用的时候是属于单进程还是多进程调用呢?

#12


还有一个办法,在你算号的那个字段上建立聚集索引,呵呵.

逼着SQL SERVER 排队插入.

#13


在你算号的那个字段上建立聚集索引,这是怎么怎么做的。我不太明白。可不可以详细说一下呢?

#14


果我把插入记录写成一个存储过程。如果多个用户同时调用这个存储过程,会不会也出现这中重复记录的事情呢?


一样会,所以我建议设置成主键或唯一键,插入冲突时,重试即可.

#15


--处理示例

--得到最新编号的函数
create function f_newid()
returns char(7) --编号位数固定,用char的检索效率高于varchar
as
begin
declare @re char(7)
select @re=max(BHID) from 表 
return(
case when @re is null then 'BH00001'
else 'BH'+right('0000'+cast(cast(right(@re,5) as int)+1 as varchar),5) 
end)
end
go

--测试的表,表名与函数中的表名对应
create table 表(
BHID char(7) default dbo.f_newid() --设置默认值,自动生成编号
primary key,  --设置成主键,防止编号冲突
-- constraint UNIQUE_BHID_表 unique, --如果用唯一约束,则删除上面的主键约束语句,改用此句
txt1 varchar(10),
txt2 varchar(10),
num  float)
go

/*--插入数据时,就可以不理会编号字段,直接用这样的语句
多用户同时插入时,如果编号重复,就会有错误发生
此时,前台程序拦截错误,如果是违反约束的错误
只需要重新执行插入的语句即可,此时的编号会自动再重新生成
而重新执行插入语句也很方便,因为根本就不需要改语句
--*/
insert 表(txt1,txt2,num) values('aa','bb',1)

go
--删除测试
drop table 表
drop function f_newid

#16


假设bh10位:

insert test(bh,a,b,c)
select left(max(bh),2)+right('000000000'+cast(cast(substring(max(bh),3,10) as int)+1 as varchar(10)),8),a='a',b=2,c=getdate()

#17


用com+事务处理,使追加记录事务同步,com+事务让系统做。

 zjcxc(邹建)的方法有问题,。假如几个请求同时发生到达,f_newid()同时运行,取了同样id,会插入同样id?.前台程序拦截错误,客户重新执行插入的语句,有可能造成某客户长期等待危险。

#18


假如几个请求同时发生到达,f_newid()同时运行,取了同样id,会插入同样id? 
约束会阻止此插入操作



前台程序拦截错误,客户重新执行插入的语句,有可能造成某客户长期等待危险

如果用表级锁,那客户的等待时间更长,而且影响正常的读表操作.

#19


to zjcxc(邹建):
我再问一句,用你的办法用insert into的方法插入,怎么能得到最后插入的一条记录的编号呢?

#1


不是很明白你的意思
我觉得你可以建一个表
ID     flag
00001  0
00002  0
......


然后都从这个表中取ID,更新flag=1,不知能不能解决你的问题。

#2


将编号字段设置成主键或唯一键.

插入的时候,如果出现冲突,就再重新取编号,再插入就可以了.

这样的效率高于用锁. 因为用锁要锁住整个表,直到插入操作完成. 这样,即使正常的读数据也会受到影响到.

#3


这一点sybase的处理比sql server好,因为sybase可以支持表锁、页锁、行锁等多种方式。

#4


设置主键。

#5


可設一自動增加的id 做為主健,在增加記錄時先增加id 然后讀出id 再更新其它字段

#6


to: ropriest(馆主) 

你错了,SQL一样有锁,不过用锁的效率很低而已.(sybase的道理也一样)
因为你要保证取得的编号不重复的话
那么你必须在取编号时,锁住整个表,不然你无法保证编号的唯一
而锁住整个表很显然不利于多用户的并发操作.


#7


举个例子来说

不锁表的情况:
假设你要插入一条记录,那么你就取最大编号:不是N+1,然后进行插入
如果在你取编号后,插入记录前,又有另一个用户要插入记录,那很显然,因为你的记录还未插
入,那另一个用户取得的编号仍然是N+1,和你的一样,这样就导致了编号重复.


只锁最大记录的情况:
假设你要插入一条记录,那么你就取最大编号:不是N+1,然后进行插入
如果在你取编号后,插入记录前,又有另一个用户修改记录,他将某条记录修改成了N+!
那结果和上面一样,导致了编号重复.


锁整个表的情况:
因为你在取编号时,锁住了整个表,那很显然,就相当于你独占了整个表,此时,可以避免编号重复,但另一个后果是,所有的用户都必须等你释放了锁后,才能使用,包括只是读取表的用户,如果迸发用户多的话,很显然严重影响效率.

#8


我觉得yosonlin() 的方法可能比较方便,但是好像不太适合我的用处了。我的数据库中已经有了很多数据了,并且这样修改对我原来的程序可能影响比较大。
zjcxc(邹建) 的办法应该也比较好,但不知这个判断冲突和重新编号的过程应该放在那里做比较好呢?在我的程序中判断还是写到存储过程中呢?
我现有的系统是从其他系统中导过来的数据,并没有实现追加记录的功能。现在我要添加追加记录的功能,所以碰到了这个问题。现有的数据库中没有任何存储过程。

#9


zjcxc(邹建) 说的很对。我就是不明白怎么作才好了。

#10


一个简单的办法,用存储过程做,把算号和插入,放在一个事务里
选号不要使用with (no lock)
例子:
--create table t1( i int )
begin tran
declare @a int
select @a = isnull( max(i),0 )+1 from t1 

insert into t1 (i) values ( @a )

commit tran

#11


to zjcxc(邹建) :如果我把插入记录写成一个存储过程。如果多个用户同时调用这个存储过程,会不会也出现这中重复记录的事情呢?

存储过程在多个用户调用的时候是属于单进程还是多进程调用呢?

#12


还有一个办法,在你算号的那个字段上建立聚集索引,呵呵.

逼着SQL SERVER 排队插入.

#13


在你算号的那个字段上建立聚集索引,这是怎么怎么做的。我不太明白。可不可以详细说一下呢?

#14


果我把插入记录写成一个存储过程。如果多个用户同时调用这个存储过程,会不会也出现这中重复记录的事情呢?


一样会,所以我建议设置成主键或唯一键,插入冲突时,重试即可.

#15


--处理示例

--得到最新编号的函数
create function f_newid()
returns char(7) --编号位数固定,用char的检索效率高于varchar
as
begin
declare @re char(7)
select @re=max(BHID) from 表 
return(
case when @re is null then 'BH00001'
else 'BH'+right('0000'+cast(cast(right(@re,5) as int)+1 as varchar),5) 
end)
end
go

--测试的表,表名与函数中的表名对应
create table 表(
BHID char(7) default dbo.f_newid() --设置默认值,自动生成编号
primary key,  --设置成主键,防止编号冲突
-- constraint UNIQUE_BHID_表 unique, --如果用唯一约束,则删除上面的主键约束语句,改用此句
txt1 varchar(10),
txt2 varchar(10),
num  float)
go

/*--插入数据时,就可以不理会编号字段,直接用这样的语句
多用户同时插入时,如果编号重复,就会有错误发生
此时,前台程序拦截错误,如果是违反约束的错误
只需要重新执行插入的语句即可,此时的编号会自动再重新生成
而重新执行插入语句也很方便,因为根本就不需要改语句
--*/
insert 表(txt1,txt2,num) values('aa','bb',1)

go
--删除测试
drop table 表
drop function f_newid

#16


假设bh10位:

insert test(bh,a,b,c)
select left(max(bh),2)+right('000000000'+cast(cast(substring(max(bh),3,10) as int)+1 as varchar(10)),8),a='a',b=2,c=getdate()

#17


用com+事务处理,使追加记录事务同步,com+事务让系统做。

 zjcxc(邹建)的方法有问题,。假如几个请求同时发生到达,f_newid()同时运行,取了同样id,会插入同样id?.前台程序拦截错误,客户重新执行插入的语句,有可能造成某客户长期等待危险。

#18


假如几个请求同时发生到达,f_newid()同时运行,取了同样id,会插入同样id? 
约束会阻止此插入操作



前台程序拦截错误,客户重新执行插入的语句,有可能造成某客户长期等待危险

如果用表级锁,那客户的等待时间更长,而且影响正常的读表操作.

#19


to zjcxc(邹建):
我再问一句,用你的办法用insert into的方法插入,怎么能得到最后插入的一条记录的编号呢?

#20