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

标签 sql tsql merge

我正在尝试使用 T-SQL 的 MERGE 语句插入许多记录,但是当源表中有重复记录时,我的查询无法插入。故障是由以下原因引起的:

  • 目标表有一个基于两列的主键
  • 源表可能包含违反目标表主键约束的重复记录(抛出“违反主键约束”)

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

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

    编辑
    在我原来的帖子中,我只在示例表中包含了两个字段,这些字段让位于 SO friend 提供 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
    

    最佳答案

    您可以使用分析函数代替 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)
    

    关于sql - 使用 T-SQL Merge 语句时如何避免插入重复记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6592643/

    相关文章:

    r - 同时合并列表中的多个 data.frames

    python - 如果第一列匹配,则将一个 csv 中的行追加到另一个 csv

    mysql - 检查等效行是否已存在的 INSERT 语句

    php - 如何在 LIMIT 子句中应用 bindValue 方法?

    mysql - 如果我的列值在 MySQL 中以逗号显示,如何选择行?

    tsql - 在多列和谓词上包含CONTAINS的全文本搜索-AND

    mysql - 使用来自另一列的符合特定条件的值更新一列

    sql - 相当于SQL Server中Oracle的RowID

    mysql - 在其包含的学生点的表中查找学生的排名

    svn - 颠覆 : record-only merge