是否可以忽略插入中的唯一约束并插入不存在的约束?

时间:2018-07-15 14:59:39

标签: sql-server tsql

我需要在N:N关系表中插入很多行,但是如果某个键(IDTable1,IDTable2)存在,它将引发异常,并且不会插入其余键。

我正在尝试以这种方式插入数据:

insert into MyTable(IDTable1, IDTable2)
VALUES(1,2),
VALUES(1,3),
VALUES(2,4),
VALUES(4,5),
VALUES(5,7);

问题在于,如果存在例如(1,3),则不会插入其余键,但是我希望如果某个键存在,则忽略它并插入不存在的键。

我也尝试过用try / catch进行尝试:

begin try
insert into MyTable(IDTable1, IDTable2)
VALUES(1,2),
VALUES(1,3),
VALUES(2,4),
VALUES(4,5),
VALUES(5,7);
end try
begin catch
end catch

但是问题是一样的,我受到0行影响。

是否可以忽略现有键并插入不存在的键?

谢谢。

2 个答案:

答案 0 :(得分:3)

一种方法是使用MERGE;

CREATE TABLE MyTable(
      IDTable1 int
    , IDTable2 int
    , CONSTRAINT UQ_MyTable UNIQUE (IDTable1, IDTable2)
    );
INSERT INTO dbo.MyTable VALUES(1,3);
GO

MERGE dbo.MyTable AS target
USING (
VALUES
    (1,2),
    (1,3),
    (2,4),
    (4,5),
    (5,7)
) AS source(IDTable1, IDTable2) ON 
    source.IDTable1 = target.IDTable1
    AND source.IDTable2 = target.IDTable2
WHEN NOT MATCHED BY TARGET THEN
    INSERT (IDTable1, IDTable2) VALUES(source.IDTable1, source.IDTable2);
GO

这也可以与INSERT...SELECTNOT EXISTS一起完成:

INSERT INTO dbo.MyTable (IDTable1, IDTable2)
SELECT IDTable1, IDTable2
FROM (
VALUES
    (1,2),
    (1,3),
    (2,4),
    (4,5),
    (5,7)
) AS source(IDTable1, IDTable2)
WHERE NOT EXISTS(
    SELECT *
    FROM dbo.MyTable as TARGET
    WHERE target.IDTable1 = source.IDTable1
    AND target.IDTable2 = source.IDTable2
    );

编辑:

这是一个LEFT OUTER JOIN方法:

INSERT INTO [MyTable] ([IDTable1], [IDTable2])
SELECT source.[IDTable1], source.[IDTable2]
FROM MyTable AS target
LEFT JOIN (
VALUES
    (1,2),
    (1,3),
    (2,4),
    (4,5),
    (5,7)
) AS source(IDTable1, IDTable2) ON
     source.[IDTable1] = target.[IDTable1]
    AND source.[IDTable2] = target.[IDTable2]
WHERE target.[IDTable1] IS NULL;
GO

对于此特定的表,索引和数据,MERGE似乎基于最少的逻辑读取而表现最佳。使用SQL Server 2017的这三种方法的STATISTCS IO结果为:

合并:

Table 'MyTable'. Scan count 0, logical reads 22, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

不存在:

Table 'MyTable'. Scan count 1, logical reads 17, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 11, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

左外联接:

Table 'MyTable'. Scan count 1, logical reads 17, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 1, logical reads 11, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

我观察到类似的结果,但受到CLUSTERED唯一约束。

但是,从性能的角度来看,我不会一概而论MERGE始终是最好的方法。理想情况下,SQL Server将为语义上相同的查询生成相同的计划,尽管实际上并非总是如此。当特别关注性能时,您需要检查执行计划并使用代表性数据测试性能。

答案 1 :(得分:0)

在SQL中,您必须准备将整个列表一次性插入,而不尝试一次插入,然后移至下一个。

这应该为您解决问题。它会连接到您要插入的表中,而忽略任何找到匹配项的内容

INSERT INTO [MyTable] ([IDTable1], [IDTable2])
SELECT [IDTable1], [IDTable2]
FROM [MySource] as source
    LEFT JOIN [MyTable] AS duplicates
        ON source.[IDTable1] = duplicates.[IDTable1]
             AND source.[IDTable2] = duplicates.[IDTable2]
WHERE duplicates.[IDTable1] IS NULL