使用T-SQL Merge语句时如何避免插入重复记录

Jed*_*Jed 15 sql t-sql merge

我试图使用T-SQL的MERGE语句插入许多记录,但是当源表中有重复记录时,我的查询无法INSERT.失败的原因是:

  1. 目标表具有基于两列的主键
  2. 源表可能包含违反目标表的主键约束的重复记录(抛出"违反PRIMARY KEY约束")

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

这是我正在尝试解释的查询示例.下面的示例将向临时表中添加100k记录,然后尝试将这些记录插入目标表中 -

编辑 在我的原始帖子中,我只在示例表中包含了两个字段,这些字段让位给SO的朋友提供DISTINCT解决方案以避免MERGE语句中的重复.我应该提到,在我的现实问题中,表有15个字段,其中15个字段中有两个字段是CLUSTERED PRIMARY KEY.所以DISTINCT关键字不起作用,因为我需要选择所有15个字段并忽略基于两个字段的重复项.

我已经更新了下面的查询以包含一个字段col4.我需要在MERGE中包含col4,但我只需要确保只有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
Run Code Online (Sandbox Code Playgroud)

t-c*_*.dk 19

解决了您的新规范.只插入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); 
Run Code Online (Sandbox Code Playgroud)


gbn*_*gbn 8

鉴于源有重复项并且您没有完全使用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)
Run Code Online (Sandbox Code Playgroud)

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

这让我想起了"万圣节问题",其中原子操作的早期数据更改会影响以后的数据更改:这是不正确的

  • @Jed:如果2行的密钥相同,为什么其他列(更新的col4)不同?例如,这意味着一个不完整的密钥.没有解决方案,因为我们永远不知道要采取哪两行...... (3认同)

Hai*_*han 6

您可以使用分析函数代替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)
Run Code Online (Sandbox Code Playgroud)