Como faço para excluir de várias tabelas usando INNER JOIN no servidor SQL

117

No MySQL você pode usar a sintaxe

DELETE t1,t2 
FROM table1 AS t1 
INNER JOIN table2 t2 ...
INNER JOIN table3 t3 ...

Como faço a mesma coisa no SQL Server?

Byron Whitlock
fonte

Respostas:

119

Você pode tirar proveito da pseudotabela "excluída" neste exemplo. Algo como:

begin transaction;

   declare @deletedIds table ( id int );

   delete from t1
   output deleted.id into @deletedIds
   from table1 as t1
    inner join table2 as t2
      on t2.id = t1.id
    inner join table3 as t3
      on t3.id = t2.id;

   delete from t2
   from table2 as t2
    inner join @deletedIds as d
      on d.id = t2.id;

   delete from t3
   from table3 as t3 ...

commit transaction;

Obviamente, você pode fazer uma 'saída excluída'. na segunda, exclua também, se precisar de algo para se juntar à terceira mesa.

Como uma nota lateral, você também pode inserir. * Em uma instrução de inserção e ambos inseridos. * E deletados. * Em uma instrução de atualização.

EDIT: Além disso, você considerou adicionar um gatilho na tabela 1 para excluir da tabela 2 + 3? Você estará dentro de uma transação implícita e também terá as pseudotabelas "inseridas " e "excluídas " disponíveis.

John Gibb
fonte
2
É melhor apenas DELETE FROM table1 WHERE id = x e, em seguida, excluir da próxima tabela em vez de usar a junção interna e passar por todo esse texto extra ?? Basicamente, pulando a junção interna, preciso apenas de 2 consultas simples ... Ou esse método é mais eficiente?
Colandus
Acho que depende de quão complicada é a sua cláusula where. Para um complicado, isso seria melhor porque só acontece uma vez. Mas para uma cláusula where mais simples que afeta muitas linhas, sua proposta provavelmente seria mais eficiente, já que não precisa conter muitos ids em uma variável de tabela.
John Gibb de
@JohnGibb, Como funciona essa resposta? Você pode explicar essa resposta para que um desenvolvedor MySQL possa entendê-la?
Pacerier,
@Pacerier Não estou muito familiarizado com o MySQL. A ideia é que a primeira exclusão é apenas excluir da tabela1, mas está salvando os IDs que foram excluídos em uma variável. As duas instruções subsequentes como usa essa variável para excluir as linhas associadas da tabela 2 e da tabela 3.
John Gibb,
@JohnGibb, Agora está claro. Você deve incluir isso na resposta.
Pacerier
15
  1. Você sempre pode configurar exclusões em cascata nas relações das tabelas.

  2. Você pode encapsular as várias exclusões em um procedimento armazenado.

  3. Você pode usar uma transação para garantir uma unidade de trabalho.

Aaron Daniels
fonte
3
Definitivamente possível excluir em uma instrução de junção, eu só quero excluir de mais de uma tabela ao mesmo tempo.
Byron Whitlock,
Resposta errada, junções podem ser usadas com delete
rboarman
ad 1.) Isso não é verdade, pode nem sempre ser possível. Existem alguns cenários em que você não pode configurar exclusões em cascata, por exemplo, ciclos ou vários caminhos em cascata. (consulte stackoverflow.com/a/3548225/108374 por exemplo)
Tom Pažourek
15

Você pode usar a sintaxe JOIN na cláusula FROM em DELETE no SQL Server, mas ainda assim exclui apenas da primeira tabela e sua extensão Transact-SQL proprietária, que é alternativa à subconsulta.

Do exemplo aqui :

 -- Transact-SQL extension
 DELETE 
   FROM Sales.SalesPersonQuotaHistory 
     FROM Sales.SalesPersonQuotaHistory AS spqh INNER JOIN 
          Sales.SalesPerson AS sp ON spqh.BusinessEntityID = sp.BusinessEntityID
    WHERE sp.SalesYTD > 2500000.00;
topchef
fonte
3
Exemplo D: DELETE FROM Sales.SalesPersonQuotaHistory FROM Sales.SalesPersonQuotaHistory AS spqh INNER JOIN Sales.SalesPerson AS sp ON spqh.BusinessEntityID = sp.BusinessEntityID WHERE sp.SalesYTD> 2500000,00;
Mark A
11

Exemplo para excluir alguns registros da tabela mestre e os registros correspondentes de duas tabelas de detalhes:

BEGIN TRAN

  -- create temporary table for deleted IDs
  CREATE TABLE #DeleteIds (
    Id INT NOT NULL PRIMARY KEY
  )

  -- save IDs of master table records (you want to delete) to temporary table    
  INSERT INTO #DeleteIds(Id)
  SELECT DISTINCT mt.MasterTableId
  FROM MasterTable mt 
  INNER JOIN ... 
  WHERE ...  

  -- delete from first detail table using join syntax
  DELETE d
  FROM DetailTable_1 D
  INNER JOIN #DeleteIds X
    ON D.MasterTableId = X.Id


  -- delete from second detail table using IN clause  
  DELETE FROM DetailTable_2
  WHERE MasterTableId IN (
    SELECT X.Id
    FROM #DeleteIds X
  )


  -- and finally delete from master table
  DELETE d
  FROM MasterTable D
  INNER JOIN #DeleteIds X
    ON D.MasterTableId = X.Id

  -- do not forget to drop the temp table
  DROP TABLE #DeleteIds

COMMIT
Pavel Hodek
fonte
1
Você poderia usar em SELECT INTO #DeleteIdsvez de CREATE TABLE 'DeleteIdsseguido por INSERT INTO 'DeleteIds...?
Caltor
9

Basta saber .. isso é realmente possível no MySQL? ele vai deletar t1 e t2? ou eu apenas entendi mal a pergunta.

Mas se você apenas deseja excluir a tabela1 com várias condições de junção, apenas não crie um alias para a tabela que deseja excluir

isto:

DELETE t1,t2 
FROM table1 AS t1 
INNER JOIN table2 t2 ...
INNER JOIN table3 t3 ...

deve ser escrito assim para funcionar em MSSQL:

DELETE table1
FROM table1 
INNER JOIN table2 t2 ...
INNER JOIN table3 t3 ...

para contrastar como os outros dois RDBMS comuns fazem uma operação de exclusão:

http://mssql-to-postgresql.blogspot.com/2007/12/deleting-duplicates-in-postgresql-ms.html

Michael Buen
fonte
Obrigado pela dica do SQL Server, eu tive que ajustar o SQL ao longo dessas linhas.
Pauk
7

Basicamente, não, você precisa fazer três instruções delete em uma transação, primeiro os filhos e depois os pais. Configurar exclusões em cascata é uma boa ideia se isso não for uma coisa única e sua existência não conflitar com qualquer configuração de acionador existente.

Yishai
fonte
Eu esperava não ter que fazer isso, suponho que terei que selecionar os IDs em uma tabela temporária, já que o relacionamento não é pai e filho. depois que as linhas de uma tabela acabam, não há como obter as outras linhas.
Byron Whitlock,
3

No servidor SQL, não há como excluir várias tabelas usando join. Então você tem que deletar do filho antes de deletar o pai do formulário.

muito pequeno
fonte
2

Esta é uma forma alternativa de excluir registros sem deixar órfãos.

Declare @user Table (keyValue int, someString varchar (10))
inserir em @user
valores (1, '1 valor')

inserir em @user
valores (2, '2 valor')

inserir em @user
valores (3, '3 valor')

Declare @password Table (keyValue int, details varchar (10))
inserir em @password
valores (1, '1 senha')
inserir em @password
valores (2, '2 senha')
inserir em @password
valores (3, '3 senha')

        --antes da exclusão
  selecione * em @password a inner join @user b
                em a.keyvalue = b.keyvalue
  selecione * em #deletedID de @user onde keyvalue = 1 - funciona como o exemplo de saída
  delete @user onde keyvalue = 1
  delete @password onde keyvalue in (selecione keyvalue em #deletedid)

  --Após a exclusão--
  selecione * em @password a inner join @user b
                em a.keyvalue = b.keyvalue

escondido
fonte
2

Tudo foi apontado. Basta usar DELETE ON CASCADEno pai tableou excluir do child-tablepara parent.

Kayode
fonte
O que você quer dizer com excluir da tabela filho para o pai? você quer dizer usando técnica de junções como a mostrada na pergunta ou nas respostas citadas?
Imran Faruqi
1

Como Aaron já apontou, você pode definir o comportamento de exclusão para CASCADE e isso excluirá os registros filhos quando um registro pai for excluído. A menos que você queira que algum outro tipo de mágica aconteça (nesse caso, os pontos 2, 3 da resposta de Aaron seriam úteis), não vejo por que você precisaria excluir com junções internas.

Peter Perháč
fonte
0

Para desenvolver a resposta de John Gibb, para excluir um conjunto de dados em duas tabelas com uma relação FK:

--*** To delete from tblMain which JOINs to (has a FK of) tblReferredTo's PK  
--       i.e.  ON tblMain.Refer_FK = tblReferredTo.ID
--*** !!! If you're CERTAIN that no other rows anywhere also refer to the 
--      specific rows in tblReferredTo !!!
BEGIN TRAN;

    --*** Keep the ID's from tblReferredTo when we DELETE from tblMain
    DECLARE @tblDeletedRefs TABLE ( ID INT );
    --*** DELETE from the referring table first
    DELETE FROM tblMain 
    OUTPUT DELETED.Refer_FK INTO @tblDeletedRefs  -- doesn't matter that this isn't DISTINCT, the following DELETE still works.
    WHERE ..... -- be careful if filtering, what if other rows 
                --   in tblMain (or elsewhere) also point to the tblReferredTo rows?

    --*** Now we can remove the referred to rows, even though tblMain no longer refers to them.
    DELETE tblReferredTo
    FROM   tblReferredTo INNER JOIN @tblDeletedRefs Removed  
            ON tblReferredTo.ID = Removed.ID;

COMMIT TRAN;
AjV Jsy
fonte
-3
DELETE     TABLE1 LIN
FROM TABLE1 LIN
INNER JOIN TABLE2 LCS ON  CONDITION
WHERE CONDITION
UMA CORRIDA
fonte
não será excluído de duas ou mais tabelas. Por favor, entenda a pergunta
Kamran Shahid
-5

$ sql = "DELETE FROM basic_tbl, education_tbl, personal_tbl, address_tbl, department_tbl UTILIZAÇÃO basic_tbl, education_tbl, personal_tbl, address_tbl, department_tbl ONDE b_id= e_id= p_id= a_id= d_id=" $ id. ".' "; $ rs = mysqli_query ($ con, $ sql);

Dharmesh
fonte
Corrija sua formatação e forneça uma breve descrição de por que seu código funciona.
Bryan Herbst