如果不存在,则插入


13

我在存储过程中的插入出现并发问题。该过程的相关部分是:

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    insert into table1 (othervalue) values (@_othervalue)
    select @_id = Id from table1 where othervalue = @_othervalue
END

当我们同时运行3或4个这些存储的proc时,有时会得到多个插入。

我打算像这样修复此问题:

insert into table1 (othervalue) 
    select TOP(1) @_othervalue as othervalue from table1 WITH(UPDLOCK) 
    where NOT EXISTS ( select * from table1 where othervalue = @_othervalue )

select @_id = Id from table1 where othervalue = @_othervalue

问题是,如何在sql服务器中并发插入而不重复?我必须使用TOP只插入一次的事实困扰着我。


1
您不必使用TOP。从SELECT语句中删除FROM表引用。
ErikE 2012年


@GSerg我认为您是正确的。
克里斯

Answers:


7

您可以使用带有serializable提示的合并语句。

merge table1 with (serializable) as T 
using (select @_othervalue as othervalue) as S
on T.othervalue = S.othervalue
when not matched then
  insert (othervalue) values (othervalue);

您是否从两个或多个连接中进行压力测试?
AK

2
@AlexKuznetsov-我刚才做了另一个有关SO的问题。我在SSMS中使用了两个标签。首先测试该insert ... where not exist ...模式,发现您可能会遇到死锁和键冲突,因此有必要使用updlock和可序列化。然后,我测试了merge语句,并认为它会更好地处理事情,并且这样做是因为没有死锁,但是我仍然必须使用可序列化来避免键冲突。
Mikael Eriksson '02

1
这是一个非常棒的答案。
克里斯·马里西奇

5

如果您不想在“ othervalue”列上重复,可以unique constraint在该列上创建一个。该查询将是:

 ALTER TABLE table1
 ADD CONSTRAINT unique_c_othervalue UNIQUE(othervalue)

如果查询试图将重复的值插入“ othervalue”列中,则会抛出错误。


如果唯一约束是两行元组,那将如何工作?
克里斯

1
@Chris您如何拥有跨行的唯一约束?
亚伦·伯特兰

@Aaron我可能没有使用我的术语,但是我们有两行需要一起唯一。我认为它不是在我们的架构中强制执行的。
克里斯

2

使用@StanleyJohns建议的唯一约束。然后在插入语句周围使用BEGIN TRY END TRY。

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    BEGIN TRY
        insert into table1 (othervalue) values (@_othervalue)
        select @_id = Id from table1 where othervalue = @_othervalue        
    END TRY
    BEGIN CATCH
        select @_id = Id from table1 where othervalue = @_othervalue        
    END CATCH
END
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.