【问题标题】:Avoid duplicates in INSERT INTO SELECT query in SQL Server避免在 SQL Server 中的 INSERT INTO SELECT 查询中重复
【发布时间】: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 语句。

【问题讨论】:

    标签: sql sql-server tsql sql-insert


    【解决方案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 中包装。
              猜你喜欢
              • 2020-03-16
              • 2012-01-23
              • 1970-01-01
              • 2021-02-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多