我们有一个规则引擎,用于对文章进行分类。 当前存在一个问题,我们收到以下错误:
无法在具有唯一索引'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 (...)
应该可以防止这种情况的发生?
还有其他防止这种异常的可能性吗?
预先感谢
答案 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);