避免在 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
根据您要保留的表的Id
和name
配对,将Table1
切换为Table2
。
【讨论】:
请不要这样做。您基本上是在说“我拥有的任何数据都一文不值,让我们插入这些新数据!” @Andir 如果由于某种原因“Table2”不应该在“INSERT”之后被删除,那么使用其他方法,但这是实现 OP 所要求的完全有效的方法。 有效,但在没有事务的情况下肯定会更慢并且可能会损坏。如果你走这条路,请在 TRANSaction 中包装。以上是关于避免在 SQL Server 中的 INSERT INTO SELECT 查询中重复的主要内容,如果未能解决你的问题,请参考以下文章
访问不允许SQL-Server列中的空值的INSERT或UPDATE(访问运行时错误3162)
只有在使用列列表并且 IDENTITY_INSERT 为 ON SQL Server 时,才能为表中的标识列指定显式值
SQL Server 中的表统计信息会影响 INSERT、UPDATE 性能吗?