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


109

我有以下两个表:

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

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

我需要从数据插入Table1Table2。我可以使用以下语法:

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

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

我可以这样写:

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基于某些条件的两个语句。

Answers:


201

使用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效率较低。有关更多详细信息,请参见此链接


9
只需对NOT EXISTS版本进行说明,您将需要WITH(HOLDLOCK)提示,否则将不进行任何锁定(因为没有要锁定的行!),因此另一个线程可以在您的下方插入该行。
IDisposable

3
有趣,因为我一直认为加入比子选择更快。也许这仅适用于直连接,不适用于左连接。
邓肯2010年

1
邓肯(Duncan),当关联子查询成为子查询时,连接通常更快。如果选择列表中有子查询,则连接通常会更快。
HLGEM

9
NOT EXISTS在使用复合主键时特别有用,NOT IN将无法使用
捣毁

1
@OMGPonies-您的更多详细信息链接似乎已死。您是否还有其他有用的方法?
FreeMan

36

在MySQL中,您可以执行以下操作:

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

SQL Server是否有类似的东西?


5
+1以教育我这一点。非常好的语法。绝对比我使用的短和好。不幸的是Sql server没有这个。
Ashish Gupta

13
并非完全正确。创建唯一索引时,可以将其设置为“忽略重复项”,在这种情况下,SQL Server将忽略任何添加重复项的尝试。
IamIC 2010年

2
而且SQL Server仍然不能...可悲。
Smack Jack

1
所以SQL Server仍然不能?
因格斯

8

我只是有一个类似的问题,DISTINCT关键字很神奇:

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

21
除非我完全误解你,如果你在你插入一组重复这将工作。但是,如果您要插入的集合可能是insert into表中已有数据的重复项,那将无济于事。
FreeMan

5

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

如果您第二次运行它,您将获得一个

违反主键约束错误

这是代码:

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

4

我使用IanC建议ignore Duplicates的唯一索引解决类似问题,使用Option创建索引WITH IGNORE_DUP_KEY

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

参考:index_option


4

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

在sql server中,右键单击表设计,选择“索引/键”

选择不会重复的列,然后输入唯一键


1

有点不合时宜,但是如果要将数据迁移到新表中,并且可能的重复项在原始表中,并且可能重复的列不是id,则GROUP BY可以执行以下操作:

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

-1

一个简单DELETEINSERT就足够了:

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

开关Table1用于Table2取决于哪个表的Idname配对要保留。


3
请不要这样做。您基本上是在说“无论我拥有的所有数据都一文不值,让我们只插入这些新数据!”
安迪尔

@Andir如果出于某种原因“ Table2”不应该在“ INSERT”之后删除,则可以使用其他方法,但这是实现OP要求的完美方法。
Sacro

1
有效,但肯定会较慢,并且可能在没有事务的情况下损坏。如果您走这条路线,请包裹TRANSaction。
MC9000
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.