T-SQL: Auswählen von Zeilen, die über Joins gelöscht werden sollen


494

Szenario:

Angenommen, ich habe zwei Tabellen, Tabelle A und Tabelle B. Der Primärschlüssel von Tabelle B ist eine einzelne Spalte (BId) und eine Fremdschlüsselspalte in Tabelle A.

In meiner Situation möchte ich alle Zeilen in Tabelle A entfernen, die mit bestimmten Zeilen in Tabelle B verknüpft sind: Kann ich dies über Verknüpfungen tun? Alle Zeilen löschen, die aus den Joins gezogen werden?

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

Oder bin ich dazu gezwungen:

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

Der Grund, den ich frage, scheint mir, dass die erste Option beim Umgang mit größeren Tabellen viel effizienter wäre.

Vielen Dank!

Antworten:


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

sollte arbeiten


1
Ich habe And [meine Filterbedingung] für den Join anstelle einer Where-Klausel verwendet. Ich würde mir vorstellen, dass beide funktionieren würden, aber die Filterbedingung für den Join schränkt Ihre Ergebnisse aus dem Join ein.
TheTXI

10
Eine Frage. Warum müssen wir 'DELETE TableA FROM' anstelle von 'DELETE FROM' schreiben? Ich sehe, dass es nur in diesem Fall funktioniert, aber warum?
LaBracca

66
Ich denke, weil Sie angeben müssen, aus welcher Tabelle Datensätze gelöscht werden sollen. Ich habe gerade eine Abfrage mit der Syntax ausgeführt DELETE TableA, TableB ...und dabei die relevanten Datensätze aus beiden gelöscht. Nett.
Andrew

1
In PostgreSQL funktioniert die Syntax mit Join nicht, es ist jedoch möglich, das Schlüsselwort "using" zu verwenden. DELETE from TableA a using TableB b where b.Bid = a.Bid and [my filter condition]
Bartolo-Otrit

8
In MySQL wird der Fehler "Unbekannte Tabelle 'TableA' in MULTI DELETE" angezeigt, weil Sie einen Alias ​​für TableA (a) deklariert haben. Kleine Anpassung:DELETE a FROM TableA a INNER JOIN TableB b on b.Bid = a.Bid and [my filter condition]
Masam

260

Ich würde diese Syntax verwenden

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

7
Ich bevorzuge auch diese Syntax, scheint logisch etwas sinnvoller zu sein, was los ist. Ich weiß auch, dass Sie dieselbe Art von Syntax für ein UPDATE verwenden können.
Adam Nofsinger

Ich bevorzuge es auch, weil mir die Platzierung des Tabellenalias nach dem LÖSCHEN immer intuitiver erschien, was gelöscht wird.
Jagd

14
In der Tat wird dies auch für mich bevorzugt. Insbesondere in Fällen, in denen ich tatsächlich an derselben Tabelle teilnehmen muss (z. B. zum Löschen doppelter Datensätze). In diesem Fall muss ich einen Alias ​​für die "Seite" verwenden, von der ich lösche, und diese Syntax macht deutlich, dass ich aus dem Duplikat-Alias ​​lösche.
Chris Simmons

29

Ja, du kannst. Beispiel:

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

8
Ich beziehe mich lieber auf die Tabelle in der ersten Zeile mit ihrem Alias. Das ist "Lösche eine" anstatt "Tabelle A löschen". Wenn Sie die Tabelle mit sich selbst verbinden, wird klar, welche Seite Sie löschen möchten.
Jeremy Stein

10

Ich habe versucht, dies mit einer Zugriffsdatenbank zu tun, und festgestellt, dass ich direkt nach dem Löschen ein. * Verwenden muss .

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

Aus abgelehnter ausstehender Bearbeitung: "Die UniqueRecords-Eigenschaft muss auf yes gesetzt werden, sonst funktioniert sie nicht. ( Support.microsoft.com/kb/240098 )"
StuperUser

8

In MySQL ist es fast dasselbe , aber Sie müssen den Tabellenalias direkt nach dem Wort "DELETE" verwenden:

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

2

Die obige Syntax funktioniert in Interbase 2007 nicht. Stattdessen musste ich Folgendes verwenden:

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

(Hinweis Interbase unterstützt das AS-Schlüsselwort für Aliase nicht.)


2

Ich benutze das

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

und @TheTXI Weg ist so gut wie genug, aber ich habe Antworten und Kommentare gelesen und festgestellt, dass eine Sache, die beantwortet werden muss, die Verwendung der Bedingung in der WHERE-Klausel oder als Join-Bedingung ist. Also beschloss ich, es zu testen und ein Snippet zu schreiben, fand aber keinen bedeutenden Unterschied zwischen ihnen. Sie können SQL-Skript hier sehen und wichtiger Punkt ist, dass ich es vorgezogen habe, es als Commnet zu schreiben, da dies keine genaue Antwort ist, aber es ist groß und kann nicht in Kommentare eingefügt werden, bitte entschuldigen Sie mich.

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

Wenn Sie einen guten Grund für dieses Skript haben oder ein anderes nützliches schreiben könnten, teilen Sie es uns bitte mit. Danke und hoffe diese Hilfe.


1

Angenommen, Sie haben zwei Tabellen, eine mit einem Master-Satz (z. B. Mitarbeiter) und eine mit einem untergeordneten Satz (z. B. Abhängige), und Sie möchten alle Datenzeilen in der Tabelle Abhängige entfernen, die nicht verschlüsselt werden können mit beliebigen Zeilen in der Master-Tabelle.

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)

Der Punkt, der hier zu beachten ist, ist, dass Sie nur ein 'Array' von EmpIDs aus dem Join zuerst sammeln und diesen Satz von EmpIDs verwenden, um einen Löschvorgang für die Tabelle Dependents durchzuführen.


1

In SQLite funktioniert nur etwas, das der Antwort von beauXjames ähnelt.

Es scheint darauf zurückzuführen zu sein, DELETE FROM table1 WHERE table1.col1 IN (SOME TEMPORARY TABLE); dass eine temporäre Tabelle durch SELECT und JOIN your two table erstellt werden kann. Sie können diese temporäre Tabelle basierend auf der Bedingung filtern, dass Sie die Datensätze in Tabelle 1 löschen möchten.


1

Sie können diese Abfrage ausführen: -

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

1

Der einfachere Weg ist:

DELETE TableA
FROM TableB
WHERE TableA.ID = TableB.ID

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

Minimieren Sie die Verwendung von DML-Abfragen mit Joins. Sie sollten in der Lage sein, die meisten DML-Abfragen mit Unterabfragen wie oben durchzuführen.

Im Allgemeinen sollten Verknüpfungen nur verwendet werden, wenn Sie nach Spalten in 2 oder mehr Tabellen SELECT oder GROUP benötigen. Wenn Sie nur mehrere Tabellen berühren, um eine Grundgesamtheit zu definieren, verwenden Sie Unterabfragen. Verwenden Sie für DELETE-Abfragen eine korrelierte Unterabfrage.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.