Sql Server INSERT范围问题

时间:2009-03-12 11:06:29

标签: sql-server-2005

以前可能会问过,但搜索限制搜索结果的字词真的很难......

采用以下SQL代码段:

declare @source table (id int)
declare @target table(id int primary key, sourceId int)


set nocount on

insert into @target values (0,0)

insert into @source(id) values(1)
--insert into @source(id) values(2)

set nocount off

insert into @target select (select max(id)+1 from @target), s.id from @source s

select * from @target

这显然执行没有错误,但现在取消注释第二个插入行并发生以下错误:

Msg 2627, Level 14, State 1, Line 15
Violation of PRIMARY KEY constraint 'PK__#7DB3CB72__7EA7EFAB'. Cannot insert duplicate key in object 'dbo.@target'.

我意识到插入语句很可能会对@target表的快照产生影响,因此(select max(id)+1 from @target)将始终返回值1 - 导致上面的违规错误......

除了使用游标之外,还有什么方法吗?

3 个答案:

答案 0 :(得分:1)

您可以使用标识列(这正是它们的意思)

declare @target table(id int IDENTITY(1,1), sourceId int)

如果你的问题是在执行插入之前select子句是“计算的”,那么使用单个SQL请求就无法解决这个问题

我认为这是设计上的;为了避免重复插入,必须在插入期间计算索引ID,而不是在选择期间计算。这是IDENTITY关键字的确切目的。

如果你想一次插入一个选择,你必须编写单独的请求(例如,使用游标,但你将失去原子性,并且必须使用适当的锁定关键字以避免竞争条件)

答案 1 :(得分:1)

将insert语句更改为以下内容:

  

插入@target select(选择   来自@target的max(id)+(ROW_NUMBER()   OVER(ORDER BY s.id)),s.id from   @source s

这应该适用于这个特定的情况,但我会小心一般化。

答案 2 :(得分:0)

您确定新PK值的方式是等待发生的竞争条件。 如果您的数据库处于高负载状态,并且同时插入了多个记录,那么您将获得意外结果。

为什么不直接使用标识列,让数据库处理新主标识的分配?

或者,您可以创建某种元表,该表包含数据库中每个表的记录,并且此记录包含应在表中用作主ID的下一个值。 然后,您必须确保每次创建新记录时,还要更新元表中的下一个值(并且应确保执行适当的锁定),但是,我发现此处没有附加值方法与利用身份列。