我有一个包含多个SQL语句的事务(INSERT,UPDATE和/或DELETES)。执行时,我想忽略重复错误语句并继续下一个语句。这样做的最佳方式是什么?
答案 0 :(得分:62)
我认为您正在寻找索引上的IGNORE_DUP_KEY选项。查看http://msdn.microsoft.com/en-us/library/ms186869.aspx中记录的IGNORE_DUP_KEY ON选项,该选项会导致重复插入尝试产生警告而不是错误。
答案 1 :(得分:19)
扩展您对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对它有一个独特的约束 - 因此它们被编入索引,这应该是最佳的。
答案 2 :(得分:15)
虽然我强烈建议你构建你的sql以便不尝试重复插入(Philip Kelley的代码片段可能就是你所需要的),但我想提一句,语句上的错误并不一定会导致回滚。
除非XACT_ABORT
为ON
,否则如果遇到错误,事务将不会自动回滚,除非它严重到足以终止连接。 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
,否则会在客户端上引发错误并导致回滚。如果由于一些可怕的原因你无法避免插入重复项,你应该能够在客户端上捕获错误并忽略它。
答案 3 :(得分:10)
如果通过“Ignore Duplicate Error statments”,要中止当前语句并继续执行下一个语句而不中止trnsaction,那么只需在每个语句周围输入BEGIN TRY .. END TRY:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH /*required, but you dont have to do anything */ END CATCH
...
答案 4 :(得分:8)
我想跟进以下内容:
如果99%的数据在没有错误的情况下插入而事先做出选择会导致巨大的性能下降(例如,在我的情况下,从200行/秒到20行/秒)与“哑”插入相比抓住偶然的错误。
在忽略“违反PRIMARY KEY约束”错误之后,事情又回到了其他资源的瓶颈(净空被定义为“瓶颈资源没有的东西”)。
这是我首先参与讨论的全部原因。
答案 5 :(得分:4)
行。在尝试了一些错误处理之后,我想出了如何解决我遇到的问题。
这是一个如何使这项工作的例子(让我知道是否有我遗漏的东西):
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是Duplicate Key的错误代码。
谢谢大家的快速回复和有用的建议。
答案 6 :(得分:1)
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解决方案,但是当服务器默默地忽略它们的重复键错误时,任何依赖错误来捕获问题的人都会感到神秘。
我选择菲利普凯利的解决方案的原因是,您可以提供多行数据,但实际上只有遗漏的数据:
答案 7 :(得分:1)
我来到这里是因为我试图做同样的事情;我知道我在源数据中有欺骗但只想更新目标数据而不是添加欺骗。
我认为MERGE在这里工作得很好,因为你可以更新或删除不同的东西并插入缺少的东西。
我最终这样做了,效果很好。我使用SSIS循环遍历Excel文件并将它们加载到带有dupes和all的“RAW”SQL表中。然后我运行MERGE将“原始”表与生产表合并。然后我TRUNCATE“原始”表并移动到下一个Excel文件。
答案 8 :(得分:1)
对于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)
答案 9 :(得分:0)
在主键定义期间使用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]
答案 10 :(得分:0)
你可以用临时表来解决这个问题..
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)
这可能不适用于大量数据,但对于几行它应该可以工作!
答案 11 :(得分:-1)
键必须是唯一的。不要那样做。根据需要重新设计。
(如果你试图插入,然后删除,插入失败......只需先执行删除。在任一语句中回滚错误。)