如何忽略T-SQL(SQL Server)中的“重复键”错误

我有一个包含多个SQL语句(INSERT,UPDATE和/或DELETES)的事务。 执行时,我想忽略重复错误语句,并继续下一个语句。 这样做的最好方法是什么?

尽pipe我对你的build议是为了避免重复插入(菲利普·凯利的代码片段可能是你所需要的),但是我想提一下,语句上的错误不一定会导致回滚。

除非XACT_ABORTON ,否则如果遇到错误,事务不会自动回滚,除非严重到足以终止连接。 XACT_ABORT默认为OFF

例如,下面的sql成功地将三个值插入到表中:

 create table x ( y int not null primary key ) begin transaction insert into x(y) values(1) insert into x(y) values(2) insert into x(y) values(2) insert into x(y) values(3) commit 

除非你设置了XACT_ABORT ,否则客户端会产生一个错误并导致回滚。 如果出于某种可怕的原因,你不能避免插入重复,你应该能够在客户端陷阱错误,并忽略它。

我想你正在寻找索引上的IGNORE_DUP_KEY选项。 查看http://msdn.microsoft.com/en-us/library/ms186869.aspx上logging的IGNORE_DUP_KEY ON选项,这会导致重复插入尝试产生警告而不是错误。

将你的评论扩展到SquareCog的回复,你可以这样做:

 INSERT INTO X VALUES(Y,Z) WHERE Y NOT IN (SELECT Y FROM X) INSERT INTO X2 VALUES(Y2,Z2) WHERE Y2 NOT IN (SELECT Y FROM X2) INSERT INTO X3 VALUES(Y3,Z3) WHERE Y3 NOT IN (SELECT Y FROM X3) 

在这里,我假设Y列出现在所有三个表格中。 请注意,如果表在Y上没有编制索引,性能将会很差。

哦,是的,Y有一个独特的约束 – 所以他们索引,这应该performance最佳。

如果通过“忽略重复错误语句”,放弃当前语句并继续下一个语句而不中止trnsaction,则只需在每个语句周围放置BEGIN TRY .. END TRY:

 BEGIN TRY INSERT ... END TRY BEGIN CATCH /*required, but you dont have to do anything */ END CATCH ... 

我想用下面的语句来表示:如果99%的数据要插入而没有错误,事先select会导致巨大的性能下降(就我的情况而言,从200线/秒到20线/秒在我的情况下)相比,“哑”插入和捕捉偶尔的错误。

在忽略“违反主键约束”的错误之后,事情又回到了被其他资源瓶颈的状态(净空被定义为“瓶颈资源不具备的东西”)。

我首先从这个讨论中解决了这个问题。

好。 在尝试了一些error handling之后,我想出了如何解决我遇到的问题。

这是一个如何使这项工作的例子(让我知道如果有什么我失踪):

 SET XACT_ABORT OFF ; -- > really important to set that to OFF BEGIN DECLARE @Any_error int DECLARE @SSQL varchar(4000) BEGIN TRANSACTION INSERT INTO Table1(Value1) VALUES('Value1') SELECT @Any_error = @@ERROR IF @Any_error<> 0 AND @Any_error<>2627 GOTO ErrorHandler INSERT INTO Table1(Value1) VALUES('Value1') SELECT @Any_error = @@ERROR IF @Any_error<> 0 AND @Any_error<>2627 GOTO ErrorHandler INSERT INTO Table1(Value1) VALUES('Value2') SELECT @Any_error = @@ERROR IF @Any_error<> 0 AND @Any_error<>2627 GOTO ErrorHandler ErrorHandler: IF @Any_error = 0 OR @Any_error=2627 BEGIN PRINT @ssql COMMIT TRAN END ELSE BEGIN PRINT @ssql ROLLBACK TRAN END END 

由于上述交易,Table1将具有以下值Value1,Value2。

顺便说一下,2627是重复键的错误代码。

谢谢大家的及时答复和有益的build议。

 INSERT INTO KeyedTable(KeyField, Otherfield) SELECT n.* FROM (SELECT 'PossibleDupeLiteral' AS KeyField, 'OtherfieldValue' AS Otherfield UNION ALL SELECT 'PossibleDupeLiteral', 'OtherfieldValue2' ) LEFT JOIN KeyedTable k ON k.KeyField=n.KeyField WHERE k.KeyField IS NULL 

这告诉服务器寻找相同的数据(希望与检查重复键相同的快速方式),如果发现它就插入任何东西。

我也喜欢IGNORE_DUP_KEY解决scheme,但是当服务器默默地忽略他们的重复密钥错误时,任何依靠错误来解决问题的人都会感到困惑。

我select菲利普·凯利的解决scheme的原因是,你可以提供几行数据,只有实际上丢失的数据:

密钥必须是唯一的。 不要这样做。 根据需要重新devise。

(如果你试图插入,然后删除,插入失败…只是先删除,在任何语句中的错误回滚)。

我来到这里是因为我试图做同样的事情; 我知道我在源数据中有愚蠢的想法,只是想更新目标数据而不是添加这些数据。

我认为MERGE在这里工作的很好,因为你可以更新或删除不同的东西,并插入缺less的东西。

我最终这样做,它运作良好。 我使用SSIS循环遍历Excel文件,并将它们加载到“RAW”SQL表格中,使用dupe和all。 然后我运行一个MERGE合并“原始”表与生产表。 然后我截断“原始”表并移动到下一个Excel文件。

在主键定义期间使用IGNORE_DUP_KEY = OFF在插入时忽略重复项。 例如

 create table X( col1.....) CONSTRAINT [pk_X] PRIMARY KEY CLUSTERED ( )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 70) ON [PRIMARY] ) ON [PRIMARY] 

对于SQL Server 2000:

  INSERT INTO t1 (ID, NAME) SELECT valueid, valuename WHERE NOT EXISTS (SELECT 0 FROM t1 as t2 WHERE t2.ID = valueid AND t2.name = valuename) 

那么你可以解决这个临时表..

 DECLARE @RoleToAdds TABLE ([RoleID] int, [PageID] int) INSERT INTO @RoleToAdds ([RoleID], [PageID]) VALUES (1, 2), (1, 3), (1, 4), (2, 5) INSERT INTO [dbo].[RolePages] ([RoleID], [PageID]) SELECT rta.[RoleID], rta.[PageID] FROM @RoleToAdds rta WHERE NOT EXISTS (SELECT * FROM [RolePages] rp WHERE rp.PageID = rta.PageID AND rp.RoleID = rta.RoleID) 

这可能不适用于大量的数据,但几行应该工作!