【问题标题】:Skip-over/ignore duplicate rows on insert跳过/忽略插入时的重复行
【发布时间】:2012-05-22 14:08:47
【问题描述】:

我有以下表格:

DataValue

DateStamp    ItemId   Value
----------   ------   -----
2012-05-22   1        6541
2012-05-22   2        12321
2012-05-21   3        32

tmp_holding_DataValue

DateStamp    ItemId   Value
----------   ------   -----
2012-05-22   1        6541
2012-05-22   4        87
2012-05-21   5        234

DateStampItemId 是主键列。

我正在执行全天定期运行的插入操作(在存储过程中):

insert into DataValue(DateStamp, ItemId, Value)
select DateStamp, ItemId, Value from tmp_holding_DataValue;

这会将数据从保留表 (tmp_holding_DataValue) 移动到主数据表 (DataValue)。然后保留表被截断。

问题在于,在示例中,holding 表可能包含主表中已经存在的项目。由于键不允许重复值,因此过程将失败。

一种选择是在插入过程中放置​​一个 where 子句,但主数据表有 1000 万多行,这可能需要很长时间。

有没有其他方法可以让程序在尝试插入时跳过/忽略重复项?

【问题讨论】:

  • 如果保持表中的Value 列不同怎么办,例如第一行是3253 而不是6541?这仍然是重复的吗?如果不是,是您想要更新的内容(例如在源表中添加6541 + 3253)还是直接替换?
  • 值列无关紧要,如果它不同,则忽略它,该日期戳的 DataValue 中的内容应保持原样
  • 用您需要支持的 SQL Server 的最低 版本 标记您的问题也非常有用。我没有提供MERGE 解决方案,因为最初我完全不知道您使用的是什么版本。
  • @AaronBertrand - 是的,我应该提到我使用的是 2008 年(很快就会转移到 2012 年)。在那种情况下,你会在哪里合并吗?
  • 我不确定,我仍然觉得MERGE 语法令人生畏,而且我一般都不愿意推荐它。我不确定是否所有的 MERGE 错误都已修复(请参阅 list referenced by Alex K in his answer to this 2012 question)。如果您的主要目标是性能,那么由您来测试它们并确保 (a) 它们做正确的事情并且 (b) 您选择在您的环境中表现最好的 on。我们无法预测这些答案...

标签: sql-server sql-server-2008 tsql stored-procedures


【解决方案1】:
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);

【讨论】:

  • 这可行,但我想知道是否有更快的方法,以防 DataValue 表最终以 1 亿行结束
  • 如果主键是聚集的并且持有表有一个等效的索引,那么它不应该是一个问题(或者至少没有比任何其他检查重复的解决方案更多的问题)。持有表是否曾经有“旧”数据,或者您总是附加相当新的数据?您可以添加将日期限制为合理日期的 where 子句,例如两天前,如果 DateStamp 是主键中的前导列,这应该会有所帮助。但前提是您的保留表中始终有新数据。
  • 感谢我选择了这个解决方案
  • @finoutlook 如果 I/O 成为问题,或者您想限制表上的争用,您可以使用 SELECT TOP 10000 之类的东西批量运行它。由于每次迭代都会插入在以后的执行中取消资格的记录,这将允许您限制您对服务器的影响程度。如果您的目标是最终移动所有内容,则无需为ORDER BY 烦恼,因为您不再关心 SELECT 是否具有确定性。
【解决方案2】:

您可以将 PK 分配为 Ignore Duplicate Key = Yes。然后它只会给出一个警告重复键被忽略并继续。我不是在猜测。我对此进行了测试。

我发现我不能这样做的是 SMSS。必须通过脚本删除并重新创建索引。但是您可以右键单击索引,选择 drop and recreate,然后只需更改 Ignore Duplicate Key = Yes。对我来说,SMSS 并没有立即显示变化。

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

【讨论】:

  • 我在其他地方看到了这个建议,但想保持主键不变。非常关键的是,最终的 DataValue 表中没有重复项。
  • 为什么这很棘手? WITH (IGNORE_DUP_KEY = ON); 还有@finoutlook,你有没有在一张简单的桌子上尝试过这个选项?它仍然是主键,仍然不允许重复。 IGNORE_DUP_KEY 设置仅控制 SQL Server 如何处理键违规(带有异常或带有 Duplicate key was ignored. 的简单状态消息)。
  • 还有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 名称,但是哦 :-)
【解决方案3】:

SQL Server 2008+:

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

【讨论】:

  • 我确实想过使用合并,但是 DataValue 是 10m+ 行,而 tmp_holding_DataValue 大约是 2m 行,我认为这需要很长时间,因为它将检查所有数据回到开始在那张桌子上的时间......
  • @finoutlook:也就是说,你过早地优化了?试试看吧。
  • 我总是做最坏的打算,并希望最好的..!我试试看
【解决方案4】:

我遇到了一个类似的要求,最终抛出了相同的重复键错误,然后我的想法是选择多个不同的列(主),同时返回其他列,check

INSERT INTO DataValue(DateStamp, ItemId, Value)
SELECT DISTINCT DateStamp, ItemId, MAX(Value) AS Value
FROM tmp_holding_DataValue
GROUP BY DateStamp, ItemId

事实上,没有 Distinct 也可以实现目标,因为聚合函数 MAX 将选择单个值。

【讨论】:

    猜你喜欢
    • 2012-08-25
    • 2023-03-13
    • 2016-10-22
    • 2016-03-03
    • 1970-01-01
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多