2011-07-06 52 views
12

我试图使用T-SQL的MERGE语句插入许多记录,但是当源表中有重复记录时,我的查询无法插入。如何避免在使用T-SQL合并语句时插入重复记录

  1. 目标表有一个主键基于两列
  2. 源表可能包含违反目标表的主键约束(“PRIMARY KEY约束冲突”重复记录的是:失败是造成抛出)

我正在寻找一种方法来更改我的MERGE语句,以便它可以忽略源表中的重复记录和/或将尝试/捕获INSERT语句来捕获可能发生的异常(即所有其他INSERT语句将运行,而不管可能发生的少量坏蛋) - 或者,也许有更好的方法o解决这个问题?

下面是我试图解释的一个查询示例。下面的例子将增加10万点的记录到一个临时表,然后将尝试插入到目标表中的记录 -

编辑 在我原来的职位,我只包括在让路的例子表到两个领域SO朋友提供DISTINCT解决方案以避免MERGE语句中出现重复。我应该提到,在我现实世界的问题中,表格有15个字段,其中有15个字段是两个字段是一个CLUSTERED PRIMARY KEY。所以DISTINCT关键字不起作用,因为我需要选择所有15个字段并忽略基于两个字段的重复项。

我已更新下面的查询以包含一个字段col4。我需要将col4包含在MERGE中,但我只需确保只有col2和col3是唯一的。

-- Create the source table 
CREATE TABLE #tmp (
col2 datetime NOT NULL, 
col3 int NOT NULL, 
col4 int 
) 
GO 

-- Add a bunch of test data to the source table 
-- For testing purposes, allow duplicate records to be added to this table 
DECLARE @loopCount int = 100000 
DECLARE @loopCounter int = 0 
DECLARE @randDateOffset int 
DECLARE @col2 datetime 
DECLARE @col3 int 
DECLARE @col4 int 

WHILE (@loopCounter) < @loopCount 
BEGIN 
    SET @randDateOffset = RAND() * 100000 
    SET @col2 = DATEADD(MI,@randDateOffset,GETDATE()) 
    SET @col3 = RAND() * 1000 
    SET @col4 = RAND() * 10 
    INSERT INTO #tmp 
    (col2,col3,col4) 
    VALUES 
    (@col2,@col3,@col4); 

    SET @loopCounter = @loopCounter + 1 
END 

-- Insert the source data into the target table 
-- How do we make sure we don't attempt to INSERT a duplicate record? Or how can we 
-- catch exceptions? Or? 
MERGE INTO dbo.tbl1 AS tbl 
    USING (SELECT * FROM #tmp) AS src 
    ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3) 
    WHEN NOT MATCHED THEN 
     INSERT (col2,col3,col4) 
     VALUES (src.col2,src.col3,src.col4); 
GO 
+0

您必须决定在#tmp中有col2和col3重复的时候应该选择哪一行。例如,您可以使用'group by col2,col3'和'min(col4)col4'。 –

回答

15

解决您的新规范。只插入最高值的col4:这次我使用了group by来防止重复的行。

MERGE INTO dbo.tbl1 AS tbl 
USING (SELECT col2,col3, max(col4) col4 FROM #tmp group by col2,col3) AS src 
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3) 
WHEN NOT MATCHED THEN 
    INSERT (col2,col3,col4) 
    VALUES (src.col2,src.col3,src.col4); 
+0

我犯了我的查询示例中只使用两个字段的错误。事实是,我的目标表有两个以上的字段。但是,组成PK群集的只有两个字段。所以你提出的DISTINCT解决方案是不够的。我更新了原始文章以反映其他字段。无论如何,感谢您的答复(+1回答问题是)。 – Jed

+1

好吧,我给了它一个新的尝试。我希望我理解正确。 –

7

鉴于源代码有重复,并且您没有完全使用MERGE,我会使用INSERT。

INSERT dbo.tbl1 (col2,col3) 
SELECT DISTINCT col2,col3 
FROM #tmp src 
WHERE NOT EXISTS (
     SELECT * 
     FROM dbo.tbl1 tbl 
     WHERE tbl.col2 = src.col2 AND tbl.col3 = src.col3) 

MERGE失败的原因是它没有被逐行检查。所有不匹配都会被找到,然后它会尝试插入所有这些。它不检查同一批次中已经匹配的行。

这让我想起了一下"Halloween problem"的其中一个原子操作的早期数据的变化会影响以后数据的变化:这是不正确的

+0

我还没有测试过我自己的脚本,你说它失败了吗? –

+0

@ t-clausen.dk:你也有DISTINCT,所以应该没问题。鉴于MERGE的限制,为什么不使用INSERT,我认为... – gbn

+0

由于问题的标题为 –

2

取代GROUP BY,您可以使用分析函数,从而允许您选择要合并的重复记录集中的特定记录。

MERGE INTO dbo.tbl1 AS tbl 
USING (
    SELECT * 
    FROM (
     SELECT *, ROW_NUMBER() OVER (PARTITION BY col2, col3 ORDER BY ModifiedDate DESC) AS Rn 
     FROM #tmp 
    ) t 
    WHERE Rn = 1 --choose the most recently modified record 
) AS src 
ON (tbl.col2 = src.col2 AND tbl.col3 = src.col3)