T-SQL: Selecionando linhas para excluir por meio de junções

494

Cenário:

Digamos que eu tenho duas tabelas, TableA e TableB. A chave primária da TabelaB é uma coluna única (BId) e é uma coluna de chave estrangeira na TabelaA.

Na minha situação, quero remover todas as linhas da Tabela A vinculadas a linhas específicas na Tabela B: Posso fazer isso por meio de junções? Excluir todas as linhas extraídas das junções?

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

Ou sou forçado a fazer isso:

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

A razão pela qual pergunto é que me parece que a primeira opção seria muito mais eficiente ao lidar com tabelas maiores.

Obrigado!

John
fonte

Respostas:

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

Deveria trabalhar

TheTXI
fonte
1
Eu usei And [minha condição de filtro] na junção em vez da cláusula Where. Eu imagino que ambos funcionariam, mas a condição de filtro na junção limitará seus resultados da junção.
TheTXI
10
Uma questão. Por que precisamos escrever 'DELETE TableA FROM' em vez de 'DELETE FROM'? Eu vejo que funciona apenas neste caso, mas por quê?
LaBracca
66
Eu acho que porque você precisa indicar de qual tabela excluir os registros. Acabei de executar uma consulta com a sintaxe DELETE TableA, TableB ...e que realmente excluiu os registros relevantes de ambos. Agradável.
Andrew
1
Na sintaxe do PostgreSQL com join, não funciona, mas é possível usar a palavra-chave "using". DELETE from TableA a using TableB b where b.Bid = a.Bid and [my filter condition]
bartolo-otrit
8
No MySQL, você obteria o erro "Tabela desconhecida 'TabelaA' no MULTI DELETE" e isso ocorre porque você declarou um alias para a TabelaA (a). Pequeno ajuste:DELETE a FROM TableA a INNER JOIN TableB b on b.Bid = a.Bid and [my filter condition]
masam 10/08/2012
260

Eu usaria essa sintaxe

Delete a 
from TableA a
Inner Join TableB b
on  a.BId = b.BId
WHERE [filter condition]
cmsjr
fonte
7
Eu prefiro essa sintaxe também, parece fazer um pouco mais de sentido logicamente o que está acontecendo. Além disso, eu sei que você pode usar esse mesmo tipo de sintaxe para uma atualização.
Adam Nofsinger
Também prefiro, porque o posicionamento do alias da tabela após o DELETE sempre me pareceu mais intuitivo quanto ao que está sendo excluído.
Jagd
14
Na verdade, isso também é preferido para mim. Especificamente nos casos em que eu realmente preciso ingressar na mesma tabela (por exemplo, para excluir registros duplicados). Nesse caso, preciso usar um alias para o "lado" do qual estou excluindo e essa sintaxe torna super claro que estou excluindo do alias duplicado.
21712 Chris Simmons
29

Sim você pode. Exemplo:

DELETE TableA 
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]
Diadistis
fonte
8
Prefiro me referir à tabela na primeira linha por seu apelido. Isso é "Excluir um" em vez de "Excluir TabelaA". No caso em que você une a tabela, ela deixa claro de que lado você deseja excluir.
9306 Jeremy Stein
10

Estava tentando fazer isso com um banco de dados de acesso e descobri que precisava usar um. * Logo após a exclusão.

DELETE a.*
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]
Tony Emrud
fonte
Da edição pendente rejeitada: "A propriedade UniqueRecords deve ser configurada como yes, caso contrário, não funcionará. ( Support.microsoft.com/kb/240098 )"
StuperUser
8

É quase o mesmo no MySQL , mas você deve usar o alias da tabela logo após a palavra "DELETE":

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

A sintaxe acima não funciona no Interbase 2007. Em vez disso, tive que usar algo como:

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]))

(Nota: Interbase não suporta a palavra-chave AS para aliases)

DavidJ
fonte
2

Estou usando isso

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

e a maneira @TheTXI é boa o suficiente, mas leio respostas e comentários e descobri que uma coisa deve ser respondida é usar a condição na cláusula WHERE ou como condição de junção. Então, decidi testá-lo e escrever um trecho, mas não encontrei uma diferença significativa entre eles. Você pode ver o script sql aqui e o ponto importante é que eu preferi escrevê-lo como commnet, porque essa resposta não é exata, mas é grande e não pode ser colocada em comentários, por favor, me perdoe.

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())

Se você puder obter um bom motivo desse script ou escrever outro útil, compartilhe. Obrigado e espero que isso ajude.

QMaster
fonte
1

Digamos que você tenha 2 tabelas, uma com um conjunto mestre (por exemplo, funcionários) e outra com um conjunto filho (por exemplo, dependentes) e você deseja se livrar de todas as linhas de dados na tabela Dependents que não podem ser ativadas com quaisquer linhas na tabela mestre.

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)

O ponto a ser observado aqui é que você está apenas coletando uma 'matriz' de EmpIDs da junção primeiro, usando esse conjunto de EmpIDs para executar uma operação de Exclusão na tabela Dependents.

beauXjames
fonte
1

No SQLite, a única coisa que funciona é algo semelhante à resposta do beauXjames.

Parece que tudo se resume a isso DELETE FROM table1 WHERE table1.col1 IN (SOME TEMPORARY TABLE); e que alguma tabela temporária pode ser criada por SELECT e JOIN na sua tabela de dois. É possível filtrar essa tabela temporária com base na condição em que você deseja excluir os registros da Tabela1.

Bhoom Suktitipat
fonte
1

você pode executar esta consulta: -

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

A maneira mais simples é:

DELETE TableA
FROM TableB
WHERE TableA.ID = TableB.ID
Carlos Barini
fonte
1
DELETE FROM table1
where id IN 
    (SELECT id FROM table2..INNER JOIN..INNER JOIN WHERE etc)

Minimize o uso de consultas DML com associações. Você deve conseguir fazer quase todas as consultas DML com subconsultas como acima.

Em geral, as junções devem ser usadas apenas quando você precisar SELECT ou GROUP por colunas em 2 ou mais tabelas. Se você estiver tocando apenas em várias tabelas para definir uma população, use subconsultas. Para consultas DELETE, use subconsulta correlacionada.

1c1cle
fonte