我有一个对它有唯一约束的表:
create table dbo.MyTab
(
MyTabID int primary key identity,
SomeValue nvarchar(50)
);
Create Unique Index IX_UQ_SomeValue
On dbo.MyTab(SomeValue);
Go
哪个代码最好检查重复项(如果找到重复项,则成功= 0)?
选项1
Declare @someValue nvarchar(50) = 'aaa'
Declare @success bit = 1;
Begin Try
Insert Into MyTab(SomeValue) Values ('aaa');
End Try
Begin Catch
-- lets assume that only constraint errors can happen
Set @success = 0;
End Catch
Select @success
选项2
Declare @someValue nvarchar(50) = 'aaa'
Declare @success bit = 1;
IF EXISTS (Select 1 From MyTab Where SomeValue = @someValue)
Set @success = 0;
Else
Insert Into MyTab(SomeValue) Values ('aaa');
Select @success
从我的观点来看 - 我确实认为Try/Catch
是错误的,不是预期的(例如死锁,甚至是不期望重复的约束)。在这种情况下,有时用户可能会尝试提交重复内容,因此会出现错误。
我发现article by Aaron Bertrand状态 - 即使大多数插入成功,检查重复项也不会太慢。
网上还有很多建议可以使用Try / Catch(避免2个语句不是1)。在我的环境中,可能只有1%的不成功案例,所以这种情况也是有道理的。
你有什么看法?使用选项1或选项2的其他原因是什么?更新:在这种情况下我不确定它是否重要,但是表有而不是更新触发器(出于审计目的 - 行删除也通过Update语句发生)。
答案 0 :(得分:4)
我看过这篇文章,但请注意,对于低故障率,我更喜欢“JFDI”模式。我之前在大容量系统上使用过这个(40k行/秒)。
在Aaron的代码中,在高负载和大量写入时首先测试时,您仍然可以获得重复。 (在dba.se上解释)这很重要:你的副本仍然会发生,而不是经常发生。您仍然需要异常处理并知道何时忽略重复错误(2627)
编辑:Remus在另一个答案中简洁地解释了
但是,我会有一个单独的TRY / CATCH来测试仅重复错误
BEGIN TRY
-- stuff
BEGIN TRY
INSERT etc
END TRY
BEGIN CATCH
IF ERROR_NUMBER() <> 2627
RAISERROR etc
END CATCH
--more stuff
BEGIN CATCH
RAISERROR etc
END CATCH
答案 1 :(得分:3)
首先,EXISTS(SELECT ...)
是不正确的,因为它在并发下失败:多个事务可以同时运行检查 并且所有人都认为他们必须INSERT,一个将是幸运的首先插入的赢家,所有其余的将违反约束。换句话说,检查和插入之间存在竞争条件。所以你必须尝试 ,所以最好只试试/捕捉。
答案 2 :(得分:1)
请不要这样做,但是在抛出异常时可能会出现日志记录。如果你在插入之前检查没有发生这样的事情。
try / catch块应该用于可能因非确定性原因而中断的部分。我会说你在检查现有记录方面更明智,因为你知道它可能会破坏,为什么会这样。所以自己检查是从开发人员的角度来看更好的方法。
但是在你的代码中,它可能仍会在插入时中断,因为在检查时间和插入时间之间,其他一些用户已经将其插入...但这是(如前所述)非确定性错误。这就是你:
的原因exists
try/catch
另一个好处是,从代码中可以看出为什么它可以破坏,而try / catch块可以隐藏它,并且可以删除它们认为为什么这里,它只是插入记录 ...
答案 3 :(得分:1)
选项 - 3
Begin Try
SET XACT_ABORT ON
Begin Tran
IF NOT EXISTS (Select 1 From MyTab Where SomeValue = @someValue)
Begin
Insert Into MyTab(SomeValue) Values ('aaa');
End
Commit Tran
End Try
begin Catch
Rollback Tran
End Catch
答案 4 :(得分:0)
为什么不在桌面上实现INSTEAD OF INSERT
触发器?您可以检查该行是否存在,如果存在则不执行任何操作,如果不存在,则插入该行。