删除相交
DELETE WITH INTERSECT
我有两个 table 具有相同的列数但没有主键(我知道,这不是我的错)。现在我需要删除 table A 中存在于 table B 中的所有行(它们相等,每行有 30 列)。
我认为最直接的方法是 INNER JOIN
并解决我的问题。但是,所有列的写入条件(担心 NULL
)并不优雅(可能是因为我的 table 也不优雅)。
我想使用 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
但是 table @A
.
中的所有行都被删除了
这让我想到了第二个问题:为什么命令 DELETE @A FROM @B
会删除 table @A
中的所有行?
要回答你的第一个问题,你可以根据join
删除:
delete a
from @a a
join @b b on a.value = b.value and a.username = b.username
第二种情况真是奇怪。我记得这里有类似的案例,还有很多关于这种行为的投诉。我会尝试解决这个问题。
- 创建一个table(T)定义主键
- 将 A 中的所有记录插入到 T 中(我假设 A 中没有重复项)
- 尝试将 B 中的所有记录插入 T 中
3A.如果插入失败从 B 中删除它(已经存在)
- 掉T(你真的不应该!!!)
您可以使用 Giorgi 的回答来删除您需要的行。
关于为什么删除所有行的问题,那是因为没有限制条件。您的 FROM
子句得到一个 table 来处理,但是没有 WHERE
子句来防止某些行从 @A
.
中删除
Giorgi 的回答明确比较了您希望避免的所有列。
可以编写不显式列出所有列的代码。
EXCEPT
生成您需要的结果集,但我不知道将此结果集用于 DELETE
来自 A
没有主键的原始行的好方法。因此,下面的解决方案使用 SELECT * INTO
将此中间结果保存在临时 table 中。然后删除 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 更容易编写和处理。
试试这个:
DELETE a
FROM @A a
WHERE EXISTS (SELECT a.* INTERSECT SELECT * FROM @B)
从@A 中删除,其中对于@A 中的每条记录,@A 中的记录与@B 中的记录相交。
这是基于 Paul White blog post 使用 INTERSECT 进行不等式检查。
我有两个 table 具有相同的列数但没有主键(我知道,这不是我的错)。现在我需要删除 table A 中存在于 table B 中的所有行(它们相等,每行有 30 列)。
我认为最直接的方法是 INNER JOIN
并解决我的问题。但是,所有列的写入条件(担心 NULL
)并不优雅(可能是因为我的 table 也不优雅)。
我想使用 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
但是 table @A
.
这让我想到了第二个问题:为什么命令 DELETE @A FROM @B
会删除 table @A
中的所有行?
要回答你的第一个问题,你可以根据join
删除:
delete a
from @a a
join @b b on a.value = b.value and a.username = b.username
第二种情况真是奇怪。我记得这里有类似的案例,还有很多关于这种行为的投诉。我会尝试解决这个问题。
- 创建一个table(T)定义主键
- 将 A 中的所有记录插入到 T 中(我假设 A 中没有重复项)
- 尝试将 B 中的所有记录插入 T 中 3A.如果插入失败从 B 中删除它(已经存在)
- 掉T(你真的不应该!!!)
您可以使用 Giorgi 的回答来删除您需要的行。
关于为什么删除所有行的问题,那是因为没有限制条件。您的 FROM
子句得到一个 table 来处理,但是没有 WHERE
子句来防止某些行从 @A
.
Giorgi 的回答明确比较了您希望避免的所有列。
可以编写不显式列出所有列的代码。
EXCEPT
生成您需要的结果集,但我不知道将此结果集用于 DELETE
来自 A
没有主键的原始行的好方法。因此,下面的解决方案使用 SELECT * INTO
将此中间结果保存在临时 table 中。然后删除 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 更容易编写和处理。
试试这个:
DELETE a
FROM @A a
WHERE EXISTS (SELECT a.* INTERSECT SELECT * FROM @B)
从@A 中删除,其中对于@A 中的每条记录,@A 中的记录与@B 中的记录相交。
这是基于 Paul White blog post 使用 INTERSECT 进行不等式检查。