跳过/忽略插入时的重复行

fin*_*ook 25 t-sql sql-server stored-procedures sql-server-2008

我有以下表格:

DataValue

DateStamp    ItemId   Value
----------   ------   -----
2012-05-22   1        6541
2012-05-22   2        12321
2012-05-21   3        32
Run Code Online (Sandbox Code Playgroud)

tmp_holding_DataValue

DateStamp    ItemId   Value
----------   ------   -----
2012-05-22   1        6541
2012-05-22   4        87
2012-05-21   5        234
Run Code Online (Sandbox Code Playgroud)

DateStamp并且ItemId是主要的关键列.

我正在做一个插件,它会在一天中定期运行(在存储过程中):

insert into DataValue(DateStamp, ItemId, Value)
select DateStamp, ItemId, Value from tmp_holding_DataValue;
Run Code Online (Sandbox Code Playgroud)

这会将数据从保持表(tmp_holding_DataValue)移动到主数据表(DataValue)中.然后截断保持表.

问题是,如示例中所示,保持表可以包含主表中已存在的项.由于密钥不允许重复值,因此过程将失败.

一种选择是在insert proc上放置一个where子句,但主数据表有1000万+行,这可能需要很长时间.

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

Aar*_*and 28

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);
Run Code Online (Sandbox Code Playgroud)

  • 这可行,但我想知道是否有更快的事情,DataValue表最终会以1亿行结束 (3认同)
  • @finoutlook如果I/O成为一个问题,或者你想限制对表的争用,你可以使用类似`SELECT TOP 10000`的批量运行它.由于每次迭代都会插入在以后的执行中不合格的记录,因此您可以限制对服务器的影响程度.如果您的目标是最终让所有事情都移动,您不应该打扰"ORDER BY",因为您不关心SELECT是否具有确定性. (2认同)

pap*_*zzo 20

您可以将PK指定为忽略重复键=是.然后它只会给出一个警告重复键被忽略并继续.我不猜.我测试了这个.

我发现我不能这样做是SMSS.必须通过脚本删除并重新创建索引.但是您可以右键单击索引,选择drop并重新创建,然后只需更改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
Run Code Online (Sandbox Code Playgroud)

或者我认为你可以使用外连接

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
Run Code Online (Sandbox Code Playgroud)

  • 为什么这很棘手?`WITH(IGNORE_DUP_KEY = ON);`@ finoutlook你是否在一个简单的表上尝试过这个选项?它仍然是主键,并且仍然不允许重复.`IGNORE_DUP_KEY`设置仅控制SQL Server处理密钥违规的方式(带有异常或带有"复制密钥被忽略"的简单状态消息). (6认同)
  • 还有一个PK,它是强制执行的.区别在于PK违规只是一个警告,并且当Ignore Duplicate Key = Yes时它会继续插入行. (2认同)
  • 供以后参考,如果PK是集群密钥,则可以使用ALTER TABLE dbo.PKallowDup REBUILD WITH(IGNORE_DUP_KEY = ON)来完成。奇怪的是,它不允许您指定PK名称,但是好吧:-) (2认同)

Qua*_*noi 17

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
        value = t.value
*/
-- Uncomment above to rewrite duplicates rather than ignore them
Run Code Online (Sandbox Code Playgroud)

  • @finoutlook:换句话说,你过早地优化了?去尝试一下。 (2认同)