You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

执行INSERT INTO/SELECT或MERGE时主键重复错误的排查与解决

主键重复错误原因及解决方法

问题原因

目标表DESTINATION_TABLE的复合主键是DocNumber+TenantId,且执行插入时表为空,所以冲突根本不是来自目标表——是你的SOURCE_TABLE本身存在多组相同的(DocNumber, TenantId)记录。

因为SOURCE_TABLE的主键是SourceTableId(单字段唯一标识),这个主键只保证每条记录的SourceTableId不重复,但完全允许DocNumber和TenantId的组合重复。当你批量插入时,这些重复的组合就会触发目标表的主键约束,导致报错。

你可以用下面的SQL验证这一点,它会列出源表中所有(DocNumber, TenantId)重复的记录:

SELECT DocNumber, TenantId, COUNT(*) AS 重复记录数
FROM dbo.SOURCE_TABLE
WHERE DeletedById IS NULL
GROUP BY DocNumber, TenantId
HAVING COUNT(*) > 1;

执行后,报错信息里的(DOC-99, some-tenant)肯定会出现在结果里。

解决方法

根据你的业务需求,选以下一种方式处理:

1. 插入时自动去重,只保留每组重复数据的一条

比如保留每组重复记录中SourceTableId最大的那条(也就是最新插入的记录),用窗口函数实现:

INSERT INTO dbo.DESTINATION_TABLE
(
    DocNumber,
    TenantId,
    UserId,
    OtherField
)
SELECT
    po.DocNumber,
    po.TenantId,
    po.CreatedById,
    po.OtherField
FROM (
    SELECT 
        *,
        ROW_NUMBER() OVER (PARTITION BY DocNumber, TenantId ORDER BY SourceTableId DESC) AS 行号
    FROM dbo.SOURCE_TABLE
    WHERE DeletedById IS NULL
) po
WHERE po.行号 = 1;

如果业务需要保留最早的记录,把ORDER BY SourceTableId DESC改成ASC即可。

2. 先清理源表的重复数据

如果业务上不允许源表存在这类重复记录,可以先删除多余的重复项:

WITH 重复记录 AS (
    SELECT 
        SourceTableId,
        ROW_NUMBER() OVER (PARTITION BY DocNumber, TenantId ORDER BY SourceTableId DESC) AS 行号
    FROM dbo.SOURCE_TABLE
    WHERE DeletedById IS NULL
)
DELETE FROM 重复记录 WHERE 行号 > 1;

清理完成后,再用你原来的INSERT或MERGE语句插入数据就不会报错了。

3. 修改目标表约束(不推荐)

除非你确认业务上不需要(DocNumber, TenantId)的唯一性,否则不建议这么做——直接去掉目标表的复合主键,或者换成非唯一约束。但这通常会引发后续的业务逻辑问题,谨慎选择。

内容的提问来源于stack exchange,提问作者sheamus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 19:05:22