以前可能会问过,但搜索限制搜索结果的字词真的很难......
采用以下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 - 导致上面的违规错误......
除了使用游标之外,还有什么方法吗?
答案 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的下一个值。 然后,您必须确保每次创建新记录时,还要更新元表中的下一个值(并且应确保执行适当的锁定),但是,我发现此处没有附加值方法与利用身份列。