避免在 SQL Server 中的 INSERT INTO SELECT 查询中重复

Posted

技术标签:

【中文标题】避免在 SQL Server 中的 INSERT INTO SELECT 查询中重复【英文标题】:Avoid duplicates in INSERT INTO SELECT query in SQL Server 【发布时间】:2011-01-31 13:46:35 【问题描述】:

我有以下两张表:

Table1
----------
ID   Name
1    A
2    B
3    C

Table2
----------
ID   Name
1    Z

我需要将数据从Table1 插入到Table2。我可以使用以下语法:

INSERT INTO Table2(Id, Name) SELECT Id, Name FROM Table1

但是,在我的情况下,Table2 中可能存在重复的 ID(在我的情况下,它只是“1”),我不想再次复制它,因为这会引发错误。

我可以这样写:

IF NOT EXISTS(SELECT 1 FROM Table2 WHERE Id=1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 
ELSE
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 WHERE Table1.Id<>1

在不使用IF - ELSE 的情况下,有没有更好的方法来做到这一点?我想避免基于某些条件的两个INSERT INTO-SELECT 语句。

【问题讨论】:

【参考方案1】:

使用NOT EXISTS

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE NOT EXISTS(SELECT id
                    FROM TABLE_2 t2
                   WHERE t2.id = t1.id)

使用NOT IN

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE t1.id NOT IN (SELECT id
                       FROM TABLE_2)

使用LEFT JOIN/IS NULL

INSERT INTO TABLE_2
  (id, name)
   SELECT t1.id,
          t1.name
     FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2 ON t2.id = t1.id
    WHERE t2.id IS NULL

在三个选项中,LEFT JOIN/IS NULL 的效率较低。见this link for more details。

【讨论】:

只是对 NOT EXISTS 版本的澄清,您需要 WITH(HOLDLOCK) 提示,否则将不会使用任何锁(因为没有要锁定的行!)所以另一个线程可以插入该行在你之下。 很有趣,因为我一直认为加入比子选择更快。也许这仅适用于直连接,不适用于左连接。 邓肯,当它们是相关子查询时,连接通常比子选择更快。如果您在选择列表中有子查询,则连接通常会更快。 谢谢!选项2似乎真的效率低下。除非数据库足够聪明,知道不获取子查询的全部结果? NOT EXISTS 对复合主键特别有用,NOT IN 将不起作用【参考方案2】:

mysql 中你可以这样做:

INSERT IGNORE INTO Table2(Id, Name) SELECT Id, Name FROM Table1

SQL Server 有类似的吗?

【讨论】:

+1 对我进行了这方面的教育。非常好的语法。绝对比我用的更短更好。不幸的是 Sql server 没有这个。 不完全正确。创建唯一索引时,可以将其设置为“忽略重复项”,在这种情况下 SQL Server 将忽略任何添加重复项的尝试。 而 SQL Server 仍然无法...可怜。 所以SQL Server还是不行? 还是不行【参考方案3】:

我刚遇到类似的问题,DISTINCT 关键字很神奇:

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1

【讨论】:

除非我完全误解了你,否则如果你在插入 from 的集合中有重复项,这将起作用。但是,如果您从中插入的数据集可能与 insert into 表中已有的数据重复,这将无济于事。【参考方案4】:

我最近也遇到了同样的问题... 继承人在 MS SQL Server 2017 中对我有用... 主键应设置在表 2 中的 ID 上... 两个表之间的列和列属性当然应该相同。这将在您第一次运行以下脚本时起作用。表1中重复的ID,不会插入...

如果你第二次运行它,你会得到一个

违反PRIMARY KEY约束错误

这是代码:

Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1

【讨论】:

【参考方案5】:

在唯一索引as suggested by IanC here 上使用ignore Duplicates 是我针对类似问题的解决方案,使用选项WITH IGNORE_DUP_KEY 创建索引

In backward compatible syntax
, WITH IGNORE_DUP_KEY is equivalent to WITH IGNORE_DUP_KEY = ON.

参考:index_option

【讨论】:

【参考方案6】:

从 SQL Server 中,您可以在表上为(需要唯一的列)设置一个唯一键索引

【讨论】:

它不响应 INSERT INGORE INTO 的替代。【参考方案7】:

有点跑题了,但是如果你想将数据迁移到一个新的表中,并且可能的重复在原始表中,并且可能重复的列不是一个id,一个@ 987654321@会做:

INSERT INTO TABLE_2
(name)
  SELECT t1.name
  FROM TABLE_1 t1
  GROUP BY t1.name

【讨论】:

【参考方案8】:

在我的例子中,我在源表中有重复的 ID,所以没有一个提议有效。我不在乎性能,它只完成一次。 为了解决这个问题,我用光标逐一记录记录以忽略重复项。

下面是代码示例:

DECLARE @c1 AS VARCHAR(12);
DECLARE @c2 AS VARCHAR(250);
DECLARE @c3 AS VARCHAR(250);


DECLARE MY_cursor CURSOR STATIC FOR
Select
c1,
c2,
c3
from T2
where ....;

OPEN MY_cursor
FETCH NEXT FROM MY_cursor INTO @c1, @c2, @c3

WHILE @@FETCH_STATUS = 0
BEGIN
    if (select count(1) 
        from T1
        where a1 = @c1
        and a2 = @c2
        ) = 0 
            INSERT INTO T1
            values (@c1, @c2, @c3)

    FETCH NEXT FROM MY_cursor INTO @c1, @c2, @c3
END
CLOSE MY_cursor
DEALLOCATE MY_cursor

【讨论】:

【参考方案9】:

我使用 MERGE 查询来填充没有重复的表。 我遇到的问题是表中的双键(代码,值), 并且存在查询非常慢 MERGE 执行得非常快(超过 X100)

examples for MERGE query

【讨论】:

【参考方案10】:

INSERT 之前的一个简单的DELETE 就足够了:

DELETE FROM Table2 WHERE Id = (SELECT Id FROM Table1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1

根据您要保留的表的Idname 配对,将Table1 切换为Table2

【讨论】:

请不要这样做。您基本上是在说“我拥有的任何数据都一文不值,让我们插入这些新数据!” @Andir 如果由于某种原因“Table2”不应该在“INSERT”之后被删除,那么使用其他方法,但这是实现 OP 所要求的完全有效的方法。 有效,但在没有事务的情况下肯定会更慢并且可能会损坏。如果你走这条路,请在 TRANSaction 中包装。

以上是关于避免在 SQL Server 中的 INSERT INTO SELECT 查询中重复的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 中的 INSERT INTO 语句

访问不允许SQL-Server列中的空值的INSERT或UPDATE(访问运行时错误3162)

只有在使用列列表并且 IDENTITY_INSERT 为 ON SQL Server 时,才能为表中的标识列指定显式值

SQL Server 中的表统计信息会影响 INSERT、UPDATE 性能吗?

SQL SERVER 使用BULK Insert将txt文件中的数据批量插入表中

避免 SQL Server 中的 while 循环