删除INTERSECT

时间:2015-06-23 21:47:59

标签: sql sql-server sql-server-2012 sql-delete intersect

我有两个表,列数相同但没有主键(我知道,这不是我的错)。现在我需要删除表B中存在的表A中的所有行(它们是相同的,每个行有30列)。

我认为最直接的方法是做INNER JOIN并解决我的问题。但是,为所有列编写条件(担心NULL)并不优雅(可能因为我的表也不优雅)。

我想使用INTERSECT。我不知道该怎么办?这是我的第一个问题:

我试过了(SQL Fiddle):

declare @A table (value int, username varchar(20))
declare @B table (value int, username varchar(20))

insert into @A values (1, 'User 1'), (2, 'User 2'), (3, 'User 3'), (4, 'User 4')
insert into @B values (2, 'User 2'), (4, 'User 4'), (5, 'User 5')

DELETE @A 
    FROM (SELECT * FROM @A INTERSECT SELECT * from @B) A

但是从表@A中删除了所有行。

这让我想到了第二个问题:为什么命令DELETE @A FROM @B会删除表@A中的所有行?

5 个答案:

答案 0 :(得分:13)

试试这个:

DELETE a 
FROM @A a
WHERE EXISTS (SELECT a.* INTERSECT SELECT * FROM @B)

从@A删除其中,对于@A中的每条记录,都有一个匹配,其中@A中的记录与@B中的记录相交。

这是基于Paul White的blog post使用INTERSECT进行不等式检查。

SQL Fiddle

答案 1 :(得分:4)

要回答您的第一个问题,您可以根据join删除

delete a 
from @a a
join @b b on a.value = b.value and a.username = b.username

第二种情况真的很奇怪。我记得这里的类似案例和很多关于这种行为的抱怨。我会尝试解决这个问题。

答案 2 :(得分:3)

您可以使用Giorgi的答案删除您需要的行。

关于为什么所有行都被删除的问题,那是因为没有限制条件。您的FROM子句会获取要处理的表,但没有WHERE子句可以阻止从@A删除某些行。

答案 3 :(得分:0)

  1. 创建定义主键的表(T)
  2. 将A中的所有记录插入T(我假设A中没有重复)
  3. 尝试在T中插入B中的所有记录 3A。如果插入失败则从B(已存在)
  4. 中删除它
  5. 掉T(你真的不应该!!!)

答案 4 :(得分:0)

Giorgi的答案明确地比较了您想要避免的所有列。 可以编写不明确列出所有列的代码。 EXCEPT会生成您需要的结果集,但我不知道如何在不使用主键的情况下将此结果集用于DELETE来自A的原始行。因此,下面的解决方案使用SELECT * INTO将此中间结果保存在临时表中。然后删除A中的所有内容,并将临时结果复制到A。将其包裹在交易中。

-- generate the final result set that we want to have and save it in a temporary table
SELECT *
INTO #t
FROM
(
    SELECT * FROM @A
    EXCEPT
    SELECT * FROM @B
) AS E;

-- copy temporary result back into A
DELETE FROM @A;

INSERT INTO @A
SELECT * FROM #t;

DROP TABLE #t;

-- check the result
SELECT * FROM @A;

结果集

value    username
1        User 1
3        User 3

此解决方案的好处在于它使用*而不是完整的列列表。当然,您也可以明确列出所有列。编写和处理比编写所有列的比较和处理可能的NULL更容易。