Sql server 插入时跳过/忽略重复行
我有以下表格:Sql server 插入时跳过/忽略重复行,sql-server,sql-server-2008,tsql,stored-procedures,Sql Server,Sql Server 2008,Tsql,Stored Procedures,我有以下表格: DataValue DateStamp ItemId Value ---------- ------ ----- 2012-05-22 1 6541 2012-05-22 2 12321 2012-05-21 3 32 DateStamp ItemId Value ---------- ------ ----- 2012-05-22 1 6541 2012-05-22
DataValue
DateStamp ItemId Value
---------- ------ -----
2012-05-22 1 6541
2012-05-22 2 12321
2012-05-21 3 32
DateStamp ItemId Value
---------- ------ -----
2012-05-22 1 6541
2012-05-22 4 87
2012-05-21 5 234
tmp\u holding\u数据值
DateStamp ItemId Value
---------- ------ -----
2012-05-22 1 6541
2012-05-22 2 12321
2012-05-21 3 32
DateStamp ItemId Value
---------- ------ -----
2012-05-22 1 6541
2012-05-22 4 87
2012-05-21 5 234
DateStamp
和ItemId
是主键列
我正在执行一个插入操作,该操作在一天中定期运行(在存储过程中):
这会将数据从保留表(tmp\u holding\u DataValue
)移到主数据表(DataValue
)中。然后截断保留表
问题在于,如示例中所示,保留表可能包含主表中已经存在的项。由于该键不允许重复值,因此该过程将失败
一种选择是在insert proc上放置where子句,但是主数据表有1000多万行,这可能需要很长时间
有没有其他方法让过程在尝试插入时跳过/忽略重复项?在
SQL Server 2008+
中:
INSERT dbo.DataValue(DateStamp, ItemId, Value)
SELECT DateStamp, ItemId, Value
FROM dbo.tmp_holding_DataValue AS t
WHERE NOT EXISTS (SELECT 1 FROM dbo.DataValue AS d
WHERE DateStamp = t.DateStamp
AND ItemId = t.ItemId);
MERGE
INTO dataValue dv
USING tmp_holding_DataValue t
ON t.dateStamp = dv.dateStamp
AND t.itemId = dv.itemId
WHEN NOT MATCHED THEN
INSERT (dateStamp, itemId, value)
VALUES (dateStamp, itemId, value)
/*
WHEN MATCHED THEN
UPDATE SET
value = t.value
*/
-- Uncomment above to rewrite duplicates rather than ignore them
您可以将PK分配为Ignore replicate Key=Yes。然后它只会给出一个警告,重复键被忽略并继续。我不是在猜测。我测试了这个 我发现我做不到这是SMSS。必须通过脚本删除并重新创建索引。但您可以右键单击索引,选择drop and recreate,然后只需更改Ignore Duplicate Key=Yes。对我来说,SMS并没有立即显示出变化
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[PKallowDup]') AND name = N'PK_PKallowDup')
ALTER TABLE [dbo].[PKallowDup] DROP CONSTRAINT [PK_PKallowDup]
GO
USE [test]
GO
/****** Object: Index [PK_PKallowDup] Script Date: 05/22/2012 10:23:13 ******/
ALTER TABLE [dbo].[PKallowDup] ADD CONSTRAINT [PK_PKallowDup] PRIMARY KEY CLUSTERED
(
[PK] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = ON, IGNORE_DUP_KEY = ON, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
或者我想你可以使用外部连接
INSERT dbo.DataValue(DateStamp, ItemId, Value)
SELECT t.DateStamp, t.ItemId, t.Value
FROM dbo.tmp_holding_DataValue AS t
left join dbo.DataValue AS d
on d.DateStamp = t.DateStamp
AND d.ItemId = t.ItemId
WHERE d.DateStamp is null
and d.ItemId in null
我遇到了一个类似的要求,最终抛出了相同的重复键错误,然后我们的想法是选择多个不同的列(主列),同时返回其他列:
事实上,由于聚合函数MAX将选择单个值,因此该目标也可以在不使用Distinct的情况下实现。如果保留表中的
value
列不同,例如,第一行是3253
,而不是6541
?那还是复制品吗?如果不是,则是要更新的内容(例如,在源表中添加6541+3253
)还是简单地替换?值列不重要,如果它不同,则忽略它,该日期戳的数据值中已经包含的内容应该保留为isIt,用您需要支持的最低版本的SQL Server标记您的问题也是非常有用的。我没有提供MERGE
解决方案,因为最初我完全不知道您使用的是哪个版本。@AaronBertrand-是的,我应该提到我使用的是2008(很快就要到2012年了)。在这种情况下,你会使用merge而不是where吗?我不确定,我仍然觉得merge
语法让人望而生畏,一般来说,我不太愿意推荐它。我不确定是否所有的合并错误都已修复(请参阅a)。如果您的关键目标是性能,那么您需要测试它们,并确保(a)它们做了正确的事情,(b)您选择了在您的环境中性能最好的on。我们无法预测这些答案…我确实想过使用合并,但由于DataValue为1000多万行,tmp_持有的DataValue约为200万行,我认为这将需要很长时间,因为它将检查该表中的所有数据,直到时间的开始…@finoutlook:换句话说,您过早优化了吗?试试看。我总是做最坏的打算,希望是最好的。。!我会试一试这会管用的,但我想知道如果DataValue表最终有1亿行,是否还有更快的方法如果主键是集群的,并且保留表有一个等价的索引,那么它应该不会是一个问题(或者至少不会比任何其他检查重复项的解决方案更麻烦)。保留表是否有“旧”数据,或者您是否总是附加一些新的数据?您可以添加where子句,将日期限制在合理的范围内,比如两天前,如果主键中的前导列是DateStamp
,这应该会有所帮助。但前提是你总是在等待表中有新的数据。谢谢,我同意了solution@finoutlook如果I/O成为一个问题,或者您想限制表上的争用,您可以使用类似于selecttop1000
的方法批量运行它。由于每次迭代都会在以后的执行中插入不合格的记录,这将允许您限制对服务器的影响程度。如果您的目标是最终移动所有内容,那么您不必麻烦使用ORDER BY
,因为您不再关心SELECT是否是确定性的。我在别处看到了这一建议,但希望保留原样的主键。在最终的DataValue
表中没有重复项是非常关键的。为什么这很棘手<代码>带有(忽略重复键=打开)还有@finoutlook您在一个简单的表上尝试过这个选项吗?它仍然是主键,不允许重复。IGNORE\u DUP\u KEY
设置仅控制SQL Server处理密钥冲突的方式(异常情况或简单的状态消息表明Duplicate KEY已被忽略。
)。仍然存在PK,并强制执行。不同之处在于PK冲突只是一个警告,当Ignore Duplicate Key=Yes时,它会继续插入行。@AaronBertrand ALTER INDEX[PK_PKallowDup]ON[dbo]。[PKallowDup]REBUILD WITH(Ignore_DUP_Key=ON);对我来说失败了。这个语法正确吗?我说我考过了。我可以使用该条件创建表,也可以在空表上删除并创建。不认为OP想要删除他的表行。为了便于将来参考,如果PK是聚集键,则可以使用ALTER table dbo.PKallowDup REBUILD with(IGNORE_DUP_key=ON)执行此操作。奇怪的是,它不允许您指定PK名称,但哦,好吧:-)