Jak usunąć z wielu tabel za pomocą INNER JOIN na serwerze SQL

117

W MySQL możesz użyć składni

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

Jak zrobić to samo w SQL Server?

Byron Whitlock
źródło

Odpowiedzi:

119

W tym przykładzie możesz wykorzystać pseudo tabelę „usuniętą”. Coś jak:

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;

Oczywiście możesz zrobić „wyjście usunięte”. przy drugim również usuń, jeśli potrzebujesz czegoś do przyłączenia się przy trzecim stole.

Na marginesie, można również wstawić. * W instrukcji wstawiania oraz wstawiać. * I usuwać. * W instrukcji aktualizacji.

EDYCJA: Czy rozważałeś również dodanie wyzwalacza w tabeli 1 w celu usunięcia z tabeli 2 + 3? Będziesz wewnątrz niejawnej transakcji i będziesz mieć również dostępne pseudo-tabele „wstawione ” i „usunięte ”.

John Gibb
źródło
2
Czy lepiej jest po prostu DELETE FROM table1 WHERE id = x, a następnie usunąć z następnej tabeli zamiast używać sprzężenia wewnętrznego i przeglądać cały ten dodatkowy tekst? Zasadniczo, pomijając sprzężenie wewnętrzne, potrzebuję tylko 2 prostych zapytań ... A może ta metoda jest bardziej wydajna?
Colandus
Myślę, że to zależy od tego, jak skomplikowana jest twoja klauzula Where. W przypadku bardziej skomplikowanych byłoby to lepsze, ponieważ zdarza się to tylko raz. Ale dla prostszej klauzuli gdzie, która ma wpływ na wiele wierszy, propozycja byłaby prawdopodobnie bardziej wydajna, ponieważ nie musi zawierać wielu identyfikatorów w zmiennej tabeli.
John Gibb,
@JohnGibb, Jak działa ta odpowiedź? Czy możesz wyjaśnić tę odpowiedź, aby programista MySQL mógł ją zrozumieć?
Pacerier,
@Pacerier Nie znam MySQL. Chodzi o to, że pierwsze usunięcie usuwa tylko z tabeli1, ale zapisuje identyfikatory, które zostały usunięte, do zmiennej. Kolejne dwie instrukcje, w jaki sposób używa tej zmiennej do usuwania powiązanych wierszy z tabeli 2 i tabeli 3.
John Gibb
@JohnGibb, teraz to jasne. Powinieneś to uwzględnić w odpowiedzi.
Pacerier
15
  1. Zawsze możesz skonfigurować usuwanie kaskadowe w relacjach tabel.

  2. Możesz hermetyzować wiele usunięć w jednej procedurze składowanej.

  3. Możesz użyć transakcji, aby zapewnić jedną jednostkę pracy.

Aaron Daniels
źródło
3
Zdecydowanie możliwe do usunięcia w instrukcji złączenia, po prostu chcę usunąć z więcej niż jednej tabeli naraz.
Byron Whitlock,
Zła odpowiedź, łączenia mogą być używane z usuwaniem
rboarman
ad 1.) To nieprawda, nie zawsze jest to możliwe. Istnieją scenariusze, w których nie można ustawić usuwania kaskadowego, np. Cykle lub wiele ścieżek kaskadowych. (patrz na przykład stackoverflow.com/a/3548225/108374 )
Tom Pažourek
15

Możesz użyć składni JOIN w klauzuli FROM w DELETE w SQL Server, ale nadal usuwasz tylko z pierwszej tabeli i jest to zastrzeżone rozszerzenie Transact-SQL, które jest alternatywą dla zapytania podrzędnego.

Z przykładu tutaj :

 -- 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
źródło
3
Przykład D: USUŃ 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

Przykład usunięcia niektórych rekordów z tabeli głównej i odpowiednich rekordów z dwóch tabel szczegółowych:

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
źródło
1
Czy mógłbyś użyć SELECT INTO #DeleteIdszamiast CREATE TABLE 'DeleteIdspo INSERT INTO 'DeleteIds...?
Caltor
9

Zastanawiam się tylko… czy to naprawdę możliwe w MySQL? usunie t1 i t2? albo po prostu źle zrozumiałem pytanie.

Ale jeśli chcesz tylko usunąć tabelę table1 z wieloma warunkami łączenia, po prostu nie aliasuj tabeli, którą chcesz usunąć

to:

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

powinno być napisane w ten sposób, aby działało w MSSQL:

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

aby porównać, w jaki sposób dwa inne popularne RDBMS wykonują operację usuwania:

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

Michael Buen
źródło
Dzięki za wskazówkę dotyczącą SQL Server, musiałem dostosować SQL w ten sposób.
Pauk
7

Zasadniczo nie, musisz zrobić trzy oświadczenia o usunięciu w transakcji, najpierw dzieci, a potem rodzice. Konfiguracja usuwania kaskadowego jest dobrym pomysłem, jeśli nie jest to jednorazowa rzecz, a jej istnienie nie będzie kolidować z żadną istniejącą konfiguracją wyzwalacza.

Yishai
źródło
Miałem nadzieję, że nie muszę tego robić, przypuszczam, że będę musiał wybrać identyfikatory w tabeli tymczasowej, ponieważ związek nie jest relacją rodzica i dziecka. po usunięciu wierszy z jednej tabeli nie ma możliwości uzyskania innych wierszy.
Byron Whitlock
3

Na serwerze SQL nie ma możliwości usunięcia wielu tabel za pomocą łączenia. Więc musisz najpierw usunąć dziecko przed usunięciem rodzica formularza.

malutki
źródło
2

Jest to alternatywny sposób usuwania rekordów bez pozostawiania sierot.

Zadeklaruj tabelę @user (keyValue int, someString varchar (10))
wstaw do @user
wartości (1, „1 wartość”)

wstaw do @user
wartości (2, „2 wartość”)

wstaw do @user
wartości (3, „3 wartość”)

Zadeklaruj @password Table (keyValue int, details varchar (10))
wstaw do @password
wartości (1, „1 hasło”)
wstaw do @password
wartości (2, „2 Hasło”)
wstaw do @password
wartości (3, „3 Hasło”)

        - przed usunięciem
  wybierz * z @password złączenie wewnętrzne @user b
                na a.keyvalue = b.keyvalue
  select * do #deletedID z @user, gdzie keyvalue = 1 - działa to jak w przykładzie wyjściowym
  usuń @user, gdzie keyvalue = 1
  usuń @password, gdzie keyvalue w (wybierz wartość klucza z #deletedid)

  --Po usunięciu--
  wybierz * z @password złączenie wewnętrzne @user b
                na a.keyvalue = b.keyvalue

ukryty
źródło
2

Wszystko zostało wskazane. Po prostu użyj DELETE ON CASCADEna rodzicu tablelub usuń z child-tabledo parent.

kayode
źródło
Co masz na myśli, mówiąc o usunięciu z tabeli podrzędnej do rodzica? czy masz na myśli użycie techniki łączenia, takiej jak ta pokazana w pytaniu lub wyżej wymienionych odpowiedzi?
Imran Faruqi
1

Jak już wskazał Aaron, możesz ustawić zachowanie usuwania na CASCADE, co spowoduje usunięcie rekordów podrzędnych, gdy rekord nadrzędny zostanie usunięty. O ile nie chcesz, aby wydarzyła się jakaś inna magia (w takim przypadku przydatne byłyby punkty 2 i 3 odpowiedzi Aarona), nie rozumiem, dlaczego miałbyś usuwać za pomocą złączeń wewnętrznych.

Peter Perháč
źródło
0

Aby oprzeć się na odpowiedzi Johna Gibba, aby usunąć zestaw danych w dwóch tabelach z relacją 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
źródło
-3
DELETE     TABLE1 LIN
FROM TABLE1 LIN
INNER JOIN TABLE2 LCS ON  CONDITION
WHERE CONDITION
BIEG
źródło
nie usunie z dwóch lub więcej tabel. Proszę zrozumieć pytanie
Kamran Shahid
-5

$ sql = "DELETE FROM basic_tbl, education_tbl, personal_tbl, address_tbl, department_tbl UŻYWANIE basic_tbl, education_tbl, personal_tbl, address_tbl, department_tbl GDZIE b_id= e_id= p_id= a_id= d_id="" $ id.."”„; $ rs = mysqli_query ($ con, $ sql);

Dharmesz
źródło
Popraw formatowanie i podaj krótki opis, dlaczego kod działa.
Bryan Herbst