T-SQL: sélection de lignes à supprimer via des jointures

494

Scénario:

Disons que j'ai deux tables, TableA et TableB. La clé primaire de TableB est une colonne unique (BId) et est une colonne de clé étrangère dans TableA.

Dans ma situation, je souhaite supprimer toutes les lignes du tableau A qui sont liées à des lignes spécifiques du tableau B: puis-je le faire via des jointures? Supprimer toutes les lignes extraites des jointures?

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

Ou suis-je obligé de faire ceci:

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

La raison pour laquelle je pose la question est qu'il me semble que la première option serait beaucoup plus efficace lorsqu'il s'agit de tables plus grandes.

Merci!

John
la source

Réponses:

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

devrait marcher

TheTXI
la source
1
J'ai utilisé And [ma condition de filtre] sur la jointure au lieu d'une clause Where. J'imagine que les deux fonctionneraient, mais la condition de filtre sur la jointure limitera vos résultats à partir de la jointure.
TheTXI
10
Une question. Pourquoi devons-nous écrire «DELETE TableA FROM» au lieu de «DELETE FROM»? Je vois que cela ne fonctionne que dans ce cas, mais pourquoi?
LaBracca
66
Je pense que vous devez indiquer de quelle table supprimer les enregistrements. Je viens d'exécuter une requête avec la syntaxe DELETE TableA, TableB ...et cela a en fait supprimé les enregistrements pertinents des deux. Agréable.
Andrew
1
Dans PostgreSQL, la syntaxe avec join ne fonctionne pas mais il est possible d'utiliser le mot-clé "using". DELETE from TableA a using TableB b where b.Bid = a.Bid and [my filter condition]
bartolo-otrit
8
Dans MySQL, vous obtiendrez une erreur "Table inconnue 'TableA' dans MULTI DELETE" et c'est parce que vous avez déclaré un alias pour TableA (a). Petit ajustement:DELETE a FROM TableA a INNER JOIN TableB b on b.Bid = a.Bid and [my filter condition]
masam
260

J'utiliserais cette syntaxe

Delete a 
from TableA a
Inner Join TableB b
on  a.BId = b.BId
WHERE [filter condition]
cmsjr
la source
7
Je préfère également cette syntaxe, semble avoir un peu plus de sens logique ce qui se passe. De plus, je sais que vous pouvez utiliser ce même type de syntaxe pour une MISE À JOUR.
Adam Nofsinger
Je le préfère aussi, car le placement de l'alias de table après la suppression a toujours semblé plus intuitif quant à ce qui est supprimé.
Jagd
14
En effet, cela est également préféré pour moi. Plus précisément dans les cas où je dois réellement rejoindre la même table (par exemple pour supprimer des enregistrements en double). Dans ce cas, je dois utiliser un alias pour le "côté" que je supprime et cette syntaxe rend très clair que je supprime de l'alias en double.
Chris Simmons
29

Oui, vous pouvez. Exemple :

DELETE TableA 
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]
Diadistis
la source
8
Je préfère me référer au tableau de la première ligne par son alias. C'est "Supprimer un" plutôt que "Supprimer le tableau A". Dans le cas où vous joignez la table avec elle-même, cela indique clairement le côté que vous souhaitez supprimer.
Jeremy Stein
10

J'essayais de le faire avec une base de données d'accès et j'ai constaté que je devais utiliser un. * Juste après la suppression.

DELETE a.*
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]
Tony Emrud
la source
De modification en attente rejetée: « La propriété UniqueRecords doit être oui, sinon il ne fonctionnera pas (. Support.microsoft.com/kb/240098 ) »
StuperUser
8

C'est presque la même chose dans MySQL , mais vous devez utiliser l' alias de table juste après le mot "DELETE":

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

La syntaxe ci-dessus ne fonctionne pas dans Interbase 2007. Au lieu de cela, j'ai dû utiliser quelque chose comme:

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

(Remarque: Interbase ne prend pas en charge le mot clé AS pour les alias)

DavidJ
la source
2

J'utilise ceci

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

et @TheTXI est suffisant, mais j'ai lu les réponses et les commentaires et j'ai trouvé qu'une chose à laquelle il fallait répondre était d'utiliser la condition dans la clause WHERE ou comme condition de jointure. J'ai donc décidé de le tester et d'écrire un extrait, mais je n'ai pas trouvé de différence significative entre eux. Vous pouvez voir le script sql ici et le point important est que j'ai préféré l'écrire en commnet car cette réponse n'est pas exacte mais elle est grande et ne peut pas être mise dans les commentaires, veuillez me pardonner.

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

Si vous pouvez obtenir une bonne raison de ce script ou en écrire un autre utile, veuillez le partager. Merci et j'espère que cette aide.

QMaster
la source
1

Supposons que vous ayez 2 tables, une avec un ensemble maître (par exemple. Employés) et une avec un ensemble enfant (par exemple. Dépendants) et que vous souhaitez vous débarrasser de toutes les lignes de données dans la table des dépendants qui ne peuvent pas saisir avec toutes les lignes de la table principale.

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)

Le point à noter ici est que vous collectez simplement un `` tableau '' d'EmpID à partir de la jointure, en utilisant cet ensemble d'EmpID pour effectuer une opération de suppression sur la table des dépendants.

beauXjames
la source
1

Dans SQLite, la seule chose qui fonctionne est quelque chose de similaire à la réponse de beauXjames.

Il semble se résumer à cela DELETE FROM table1 WHERE table1.col1 IN (SOME TEMPORARY TABLE); et que certaines tables temporaires peuvent être créées par SELECT et JOIN vos deux tables que vous pouvez filtrer cette table temporaire en fonction de la condition que vous souhaitez supprimer les enregistrements dans Table1.

Bhoom Suktitipat
la source
1

vous pouvez exécuter cette requête: -

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

La manière la plus simple est:

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

Minimisez l'utilisation des requêtes DML avec les jointures. Vous devriez être capable de faire la plupart de toutes les requêtes DML avec des sous-requêtes comme ci-dessus.

En général, les jointures ne doivent être utilisées que lorsque vous devez sélectionner ou regrouper par colonnes dans 2 tables ou plus. Si vous touchez uniquement plusieurs tables pour définir une population, utilisez des sous-requêtes. Pour les requêtes DELETE, utilisez une sous-requête corrélée.

1c1cle
la source