2015-06-23 3 views
6

У меня есть две таблицы с одинаковым количеством столбцов без первичных ключей (я знаю, это не моя ошибка). Теперь мне нужно удалить все строки из таблицы A, которые существуют в таблице B (они равны, каждая из которых содержит 30 столбцов).УДАЛИТЬ С INTERSECT

Самое непосредственное, что я думал сделать INNER JOIN и решить мою проблему. Но условия записи для всех столбцов (беспокоиться о NULL) не изящны (может быть, мои таблицы тоже не изящны).

Я хочу использовать INTERSECT. Я не знаю, как это сделать? Это мой первый вопрос:

Я попытался (SQL Fiddle):

declare @A table (value int, username varchar(20)) 
declare @B table (value int, username varchar(20)) 

insert into @A values (1, 'User 1'), (2, 'User 2'), (3, 'User 3'), (4, 'User 4') 
insert into @B values (2, 'User 2'), (4, 'User 4'), (5, 'User 5') 

DELETE @A 
    FROM (SELECT * FROM @A INTERSECT SELECT * from @B) A 

Но были удалены все строки из таблицы @A.

Это привело меня ко второму вопросу: почему команда DELETE @A FROM @B удаляет все строки из таблицы @A?

+0

Если я хорошо помню, делая DELETE FROM A B ничего не ограничивает, и это нормально, что она удаляет все. вы можете ограничить, выполнив что-то вроде: УДАЛИТЬ ОТ B, где A.value = B.Value Добавление FROM после удаления является вроде LEFT JOIN – Xavier

+4

Вы имели в виду 'DELETE A', и это не запускается. Прямо сейчас вы удаляете @A CROSS JOIN (что-то еще). Который удаляет все, если в чем-то еще есть хотя бы одна строка. Посмотрите на план запроса, чтобы увидеть это. – usr

ответ

9

Попробуйте это:

DELETE a 
FROM @A a 
WHERE EXISTS (SELECT a.* INTERSECT SELECT * FROM @B) 

Удалить из @A, где для каждой записи в @A, есть матч, где запись в @A пересекается с записью в @B.

Это основано на blog post Пола Уайта, используя INTERSECT для проверки неравенства.

SQL Fiddle

+0

Большое спасибо. Это синтаксис, который я искал. Трудно выбрать правильный ответ, так как я задал два вопроса. Но лучший ответ на мой второй вопрос - на комментарии, данные @usr. – Nizam

4

Чтобы ответить на ваш первый вопрос, который вы можете удалить на основе join:

delete a 
from @a a 
join @b b on a.value = b.value and a.username = b.username 

Второй случай действительно странно. Я помню подобный случай и многие жалобы на это поведение. Я постараюсь уловить этот вопрос.

+0

Большое спасибо. Он работает, но я хочу избежать соединения, потому что количество столбцов в моем реальном случае. – Nizam

0
  1. Создать таблицу (T), определяющий первичные ключи
  2. вставки все записи из А в Г (я буду считать, нет дубликатов в А)
  3. попытаться вставить все записи из B в T 3A. если вставка не удается удалить его из B (уже существует)
  4. падения T (вы действительно не должны !!!)
3

Вы можете использовать ответ Джорджи, чтобы удалить строки, которые нужно.

Что касается вопроса о том, почему все строки были удалены, это потому, что нет никаких ограничений. Ваш пункт FROM получает таблицу для обработки, но нет предложения WHERE, чтобы предотвратить удаление определенных строк из @A.

+0

Недостаточно таблицы для обработки http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1/1896 –

+0

Также рассмотрите возможность использования EXCEPT вместо INTERSECT, если ваша конечная цель - удалить только одну из двух сравниваемых таблиц. Логика и шаги будут проще. –

+0

@MartinSmith - в вашем примере @A опустошен (удален ...). Это правда, что если предложение 'FROM' пустое, ничего не удаляется, но что касается того, что произошло в OP (где предложение FROM FROM НЕ пусто ...), причина такова, как я уже сказал. – Amit

0

Ответ Гиорги явно сравнивает все столбцы, которые вы хотели избежать. Можно написать код, который явно не перечисляет все столбцы. EXCEPT создает набор результатов, который вам нужен, но я не знаю, как использовать этот набор результатов в DELETE оригинальных строк из A без первичного ключа. Таким образом, приведенное ниже решение сохраняет этот промежуточный результат во временной таблице с использованием SELECT * INTO. Затем удаляет все с A и копирует временный результат в A. Оберните его в транзакцию.

-- generate the final result set that we want to have and save it in a temporary table 
SELECT * 
INTO #t 
FROM 
(
    SELECT * FROM @A 
    EXCEPT 
    SELECT * FROM @B 
) AS E; 

-- copy temporary result back into A 
DELETE FROM @A; 

INSERT INTO @A 
SELECT * FROM #t; 

DROP TABLE #t; 

-- check the result 
SELECT * FROM @A; 

набор результатов

value username 
1  User 1 
3  User 3 

Хорошая сторона этого решения заключается в том, что он использует * вместо полного списка столбцов. Конечно, вы можете также явно указать все столбцы.По-прежнему будет легче писать и обрабатывать, чем писать сравнения всех столбцов и заботиться о возможных NULL.