T-SQL: выбор строк для удаления с помощью объединений

494

Сценарий:

Допустим, у меня есть две таблицы, TableA и TableB. Первичный ключ TableB представляет собой один столбец (BId) и является столбцом внешнего ключа в TableA.

В моей ситуации я хочу удалить все строки в TableA, которые связаны с конкретными строками в TableB: можно ли это сделать с помощью объединений? Удалить все строки, которые извлекаются из объединений?

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

Или я вынужден сделать это:

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

Причина, по которой я спрашиваю, состоит в том, что мне кажется, что первый вариант был бы гораздо более эффективным при работе с большими столами.

Спасибо!

Джон
источник

Ответы:

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

должно сработать

TheTXI
источник
1
Я использовал And [условие моего фильтра] в соединении вместо предложения Where. Я полагаю, что оба будут работать, но условие фильтра при объединении ограничит ваши результаты от объединения.
TheTXI
10
Один вопрос. Почему нам нужно написать «УДАЛИТЬ таблицу А ИЗ» вместо «УДАЛИТЬ ИЗ»? Я вижу, это работает только в этом случае, но почему?
LaBracca
66
Я думаю, потому что вы должны указать, из какой таблицы удалить записи. Я только что выполнил запрос с синтаксисом, DELETE TableA, TableB ...и это фактически удалило соответствующие записи из обоих. Ницца.
Андрей
1
В PostgreSQL синтаксис с объединением не работает, но можно использовать ключевое слово «using». DELETE from TableA a using TableB b where b.Bid = a.Bid and [my filter condition]
Бартоло-Отрит
8
В MySQL вы получите сообщение об ошибке «Неизвестная таблица« TableA »в MULTI DELETE», и это потому, что вы объявили псевдоним для TableA (a). Небольшая корректировка:DELETE a FROM TableA a INNER JOIN TableB b on b.Bid = a.Bid and [my filter condition]
Масам
260

Я бы использовал этот синтаксис

Delete a 
from TableA a
Inner Join TableB b
on  a.BId = b.BId
WHERE [filter condition]
cmsjr
источник
7
Я также предпочитаю этот синтаксис, кажется, логически немного более понятный, что происходит. Кроме того, я знаю, что вы можете использовать этот же тип синтаксиса для ОБНОВЛЕНИЯ.
Адам Нофсингер
Я тоже предпочитаю это, потому что расположение псевдонима таблицы после DELETE всегда казалось мне более интуитивно понятным в отношении того, что удаляется.
Джагд
14
Действительно, это предпочтительнее и для меня. В частности, в тех случаях, когда мне нужно на самом деле присоединиться к одной таблице (например, для удаления дубликатов записей). В этом случае мне нужно использовать псевдоним для "стороны", из которой я удаляю, и этот синтаксис делает очень ясным, что я удаляю из псевдонима дубликатов.
Крис Симмонс
29

Да, ты можешь. Пример :

DELETE TableA 
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]
Diadistis
источник
8
Я предпочитаю ссылаться на таблицу в первой строке по псевдониму. То есть «Удалить», а не «Удалить таблицу А». В случае, когда вы присоединяетесь к таблице с самим собой, она дает понять, какую сторону вы хотите удалить.
Джереми Стейн
10

Пытался сделать это с базой данных доступа и обнаружил, что мне нужно использовать . * Сразу после удаления.

DELETE a.*
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]
Тони Эмруд
источник
От отвергнутого отложенного редактирования: «Свойство UniqueRecords должен быть установлен да, в противном случае он не будет работать (. Support.microsoft.com/kb/240098
StuperUser
8

Это почти то же самое в MySQL , но вы должны использовать псевдоним таблицы сразу после слова «УДАЛИТЬ»:

DELETE a
FROM TableA AS a
INNER JOIN TableB AS b
ON a.BId = b.BId
WHERE [filter condition]
Майкл Батлер
источник
2

Синтаксис выше не работает в Interbase 2007. Вместо этого мне пришлось использовать что-то вроде:

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

(Примечание. Interbase не поддерживает ключевое слово AS для псевдонимов).

DavidJ
источник
2

Я использую это

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

и способ @TheTXI достаточно хорош, но я прочитал ответы и комментарии и обнаружил, что нужно ответить на один вопрос - использовать условие в предложении WHERE или как условие соединения. Поэтому я решил проверить его и написать фрагмент кода, но не нашел значимой разницы между ними. Вы можете увидеть сценарий sql здесь, и важный момент заключается в том, что я предпочел написать его как commnet, потому что это не точный ответ, но он большой и не может быть помещен в комментарии, пожалуйста, извините.

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

Если вы могли бы получить вескую причину из этого скрипта или написать другую полезную, пожалуйста, поделитесь. Спасибо и надеюсь, что это поможет.

Qmaster
источник
1

Допустим, у вас есть 2 таблицы, одна с основным набором (например, сотрудники), а другая с дочерним набором (например, Dependents), и вы хотите избавиться от всех строк данных в таблице Dependents, которые не могут набрать ключ с любыми строками в мастер-таблице.

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)

Здесь следует отметить, что вы сначала собираете «массив» EmpID из объединения, используя этот набор EmpID для выполнения операции удаления в таблице Dependents.

beauXjames
источник
1

В SQLite единственное, что работает, это что-то похожее на ответ beauXjames.

Кажется, это сводится к тому, DELETE FROM table1 WHERE table1.col1 IN (SOME TEMPORARY TABLE); что некоторые временные таблицы могут быть созданы с помощью SELECT и СОЕДИНЯТЬ две таблицы, которые вы можете отфильтровать по этой временной таблице, исходя из условия, что вы хотите удалить записи из таблицы Table1.

Bhoom Suktitipat
источник
1

Вы можете запустить этот запрос: -

Delete from TableA 
from 
TableA a, TableB b 
where a.Bid=b.Bid
AND [my filter condition]
Адитья
источник
1

Более простой способ:

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)

Минимизируйте использование запросов DML вместе с Joins. Вы должны быть в состоянии сделать большинство запросов DML с подзапросами, как описано выше.

В общем случае объединения следует использовать только тогда, когда вам нужно выбрать или выбрать столбцы в 2 или более таблицах. Если вы только касаетесь нескольких таблиц, чтобы определить совокупность, используйте подзапросы. Для запросов DELETE используйте коррелированный подзапрос.

1c1cle
источник