T-SQL:选择要通过联接删除的行


494

场景:

假设我有两个表TableA和TableB。TableB的主键是单列(BId),并且是TableA中的外键列。

在我的情况下,我想删除TableA中与TableB中的特定行链接的所有行:是否可以通过联接做到这一点?删除从联接中拉入的所有行?

DELETE FROM TableA 
FROM
   TableA a
   INNER JOIN TableB b
      ON b.BId = a.BId
      AND [my filter condition]

还是我被迫这样做:

DELETE FROM TableA
WHERE
   BId IN (SELECT BId FROM TableB WHERE [my filter condition])

我问的原因是,在我看来,处理较大的表时,第一种选择的效率更高。

谢谢!

Answers:


722
DELETE TableA
FROM   TableA a
       INNER JOIN TableB b
               ON b.Bid = a.Bid
                  AND [my filter condition] 

应该管用


1
我在联接上使用And [我的过滤条件],而不是Where子句。我想两者都可以,但是联接上的过滤条件将限制您的联接结果。
TheTXI

10
一个问题。为什么我们需要编写“ DELETE TableA FROM”而不是“ DELETE FROM”?我看到它仅在这种情况下有效,但是为什么呢?
LaBracca 2010年

66
我认为是因为您必须指出要从中删除记录的表。我只是用语法运行了一个查询DELETE TableA, TableB ...,实际上从这两个查询中删除了相关记录。真好
安德鲁(Andrew)2010年

1
在PostgreSQL中,join语法无效,但是可以使用“ using”关键字。 DELETE from TableA a using TableB b where b.Bid = a.Bid and [my filter condition]
bartolo-otrit 2012年

8
在MySQL中,您将收到错误“ MULTI DELETE中的未知表'TableA'”,这是因为您声明了TableA(a)的别名。小调整:DELETE a FROM TableA a INNER JOIN TableB b on b.Bid = a.Bid and [my filter condition]
masam 2012年

260

我会使用这种语法

Delete a 
from TableA a
Inner Join TableB b
on  a.BId = b.BId
WHERE [filter condition]

7
我也更喜欢这种语法,从逻辑上看似乎更有意义。另外,我知道您可以对UPDATE使用相同的语法类型。
Adam Nofsinger

我也更喜欢它,因为在我看来,将表别名放在DELETE之后对于被删除的内容一直比较直观。
Jagd 2012年

14
确实,这对我来说也是首选。特别是在需要实际加入同一张表的情况下(例如,删除重复的记录)。在这种情况下,我需要为要从中删除的“边”使用别名,并且这种语法使其非常清楚我要从重复别名中删除。
克里斯·西蒙斯

29

是的你可以。范例:

DELETE TableA 
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]

8
我更喜欢通过别名来引用第一行中的表。那就是“删除一个”而不是“删除表A”。如果您将表与自身连接在一起,则可以清楚地删除要删除的那一侧。
杰里米·斯坦因


8

MySQL中几乎相同,但是必须在单词“ DELETE”之后使用表别名

DELETE a
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]

2

上面的语法在Interbase 2007中不起作用。相反,我不得不使用类似以下的内容:

DELETE FROM TableA a WHERE [filter condition on TableA] 
  AND (a.BId IN (SELECT a.BId FROM TableB b JOIN TableA a 
                 ON a.BId = b.BId 
                 WHERE [filter condition on TableB]))

(注意Interbase不支持别名的AS关键字)


2

我正在用这个

DELETE TableA 
FROM TableA a
INNER JOIN
TableB b on b.Bid = a.Bid
AND [condition]

@TheTXI方式就足够了,但是我阅读了答案和评论,发现必须回答的一件事是在WHERE子句中使用条件或作为连接条件。因此,我决定对其进行测试并编写一个代码段,但并未发现它们之间的有意义的区别。您可以在此处看到sql脚本,重要的一点是我更喜欢将其编写为commnet,因为这不是确切的答案,但是它很大并且不能放在注释中,请原谅。

Declare @TableA  Table
(
  aId INT,
  aName VARCHAR(50),
  bId INT
)
Declare @TableB  Table
(
  bId INT,
  bName VARCHAR(50)  
)

Declare @TableC  Table
(
  cId INT,
  cName VARCHAR(50),
  dId INT
)
Declare @TableD  Table
(
  dId INT,
  dName VARCHAR(50)  
)

DECLARE @StartTime DATETIME;
SELECT @startTime = GETDATE();

DECLARE @i INT;

SET @i = 1;

WHILE @i < 1000000
BEGIN
  INSERT INTO @TableB VALUES(@i, 'nameB:' + CONVERT(VARCHAR, @i))
  INSERT INTO @TableA VALUES(@i+5, 'nameA:' + CONVERT(VARCHAR, @i+5), @i)

  SET @i = @i + 1;
END

SELECT @startTime = GETDATE()

DELETE a
--SELECT *
FROM @TableA a
Inner Join @TableB b
ON  a.BId = b.BId
WHERE a.aName LIKE '%5'

SELECT Duration = DATEDIFF(ms,@StartTime,GETDATE())

SET @i = 1;
WHILE @i < 1000000
BEGIN
  INSERT INTO @TableD VALUES(@i, 'nameB:' + CONVERT(VARCHAR, @i))
  INSERT INTO @TableC VALUES(@i+5, 'nameA:' + CONVERT(VARCHAR, @i+5), @i)

  SET @i = @i + 1;
END

SELECT @startTime = GETDATE()

DELETE c
--SELECT *
FROM @TableC c
Inner Join @TableD d
ON  c.DId = d.DId
AND c.cName LIKE '%5'

SELECT Duration    = DATEDIFF(ms,@StartTime,GETDATE())

如果您可以从此脚本中获得充分的理由或编写另一个有用的内容,请分享。谢谢,希望对您有所帮助。


1

假设您有2个表,一个表具有一个主集(例如,Employees)和一个子集(例如,Dependents),并且您想要摆脱Dependents表中所有无法键入的数据行与主表中的任何行。

delete from Dependents where EmpID in (
select d.EmpID from Employees e 
    right join Dependents d on e.EmpID = d.EmpID
    where e.EmpID is null)

这里要注意的一点是,您只是从连接中首先收集了一个EmpID的“数组”,使用那组EmpID对依赖表进行了删除操作。


1

在SQLite中,唯一起作用的是类似于beauXjames的答案的东西。

似乎可以归结为这一点 DELETE FROM table1 WHERE table1.col1 IN (SOME TEMPORARY TABLE); ,可以通过SELECT和JOIN您的两个表来创建一些临时表,您可以根据要删除Table1中的记录的条件来过滤该临时表。


1

您可以运行以下查询:

Delete from TableA 
from 
TableA a, TableB b 
where a.Bid=b.Bid
AND [my filter condition]


1
DELETE FROM table1
where id IN 
    (SELECT id FROM table2..INNER JOIN..INNER JOIN WHERE etc)

通过Join最小化DML查询的使用。您应该能够使用上述子查询执行大多数DML查询。

通常,仅当需要在2个或更多表中的列进行SELECT或GROUP BY时,才应使用联接。如果触摸多个表来定义总体,请使用子查询。对于DELETE查询,请使用相关子查询。

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.