MSSQL重复键行异常

时间:2019-02-19 11:58:31

标签: sql sql-server tsql

我们有一个规则引擎,用于对文章进行分类。 当前存在一个问题,我们收到以下错误:

  

无法在具有唯一索引'IX_article_category_no_duplicates'的对象'dbo.article_category'中插入重复的键行。重复的密钥值为(123,456)。该声明已终止。

article_category中创建条目是在存储过程中进行的。该过程的主要部分是:

MERGE article_category AS [target]
USING (
    SELECT articleId, @categoryId, @creator, @now, @ruleId, 2 
    FROM @articleIdList
) AS [source] (articleId, categoryId, creator, createDate, ruleId, assignmentTypeId)
    ON (
        target.articleId = source.articleId 
        AND target.categoryId= source.categoryId
    )
WHEN NOT MATCHED THEN
    INSERT (articleId, categoryId, creator, createDate, ruleId, assignmentTypeId)
    VALUES (source.articleId, source.categoryId, source.creator, source.createDate, source.ruleId, source.assignmentTypeId);

当两个规则试图为同一文章插入相同类别时,会发生上述异常。

如何阻止这种情况的发生?我认为使用merge语句代替if not exists (...)应该可以防止这种情况的发生?

还有其他防止这种异常的可能性吗?

预先感谢

3 个答案:

答案 0 :(得分:1)

独立于合并,如果您要插入/更新的数据违反唯一约束,则在更新或插入时都会出现此错误。

merge ... using无法处理唯一索引约束。如果合并与using语句所保持的条件不匹配,则进行插入,否则进行更新操作。

就您而言,

target.articleId = source.articleId 
    AND target.categoryId= source.categoryId

不能保证唯一性,因此您将获得唯一的索引约束理解。

答案 1 :(得分:0)

是的,您回答不存在子句将帮助而不是合并,并且以某种方式修改存储的proc,使得在任何给定的时间只有一个条件可以访问该行。.这是死锁的典型情况< / p>

答案 2 :(得分:0)

您需要使用SOURCE列中没有重复项的UNIQUE。您可以将ROW_NUMBER()PARTITION BY <unique column>和后验过滤器一起使用,以在实际插入之前删除可能重复的内容:

;WITH DuplicateRanking AS
(
    SELECT 
        articleId = articleId, 
        categoryId = @categoryId, 
        creator = @creator, 
        createDate = @now, 
        ruleId = @ruleId, 
        assignmentTypeId = 2 ,
        DuplicateRanking = ROW_NUMBER() OVER (
            PARTITION BY 
                articleId       -- Your unique columns here
            ORDER BY 
                (SELECT NULL))  -- Your desired order here (will determine which row gets inserted)
    FROM 
        @articleIdList  
)
MERGE article_category AS [target]
USING DuplicateRanking AS [source]
    ON (
        target.articleId = source.articleId AND 
        target.categoryId= source.categoryId
    )
WHEN NOT MATCHED AND [source].DuplicateRanking = 1 -- Only insert 1 row per unique column set 
    THEN
    INSERT (articleId, categoryId, creator, createDate, ruleId, assignmentTypeId)
    VALUES (source.articleId, source.categoryId, source.creator, source.createDate, source.ruleId, source.assignmentTypeId);