我有两个表,列数相同但没有主键(我知道,这不是我的错)。现在我需要删除表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
中的所有行?
答案 0 :(得分:13)
试试这个:
DELETE a
FROM @A a
WHERE EXISTS (SELECT a.* INTERSECT SELECT * FROM @B)
从@A删除其中,对于@A中的每条记录,都有一个匹配,其中@A中的记录与@B中的记录相交。
这是基于Paul White的blog post使用INTERSECT进行不等式检查。
答案 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)
答案 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更容易。