2015-06-23 13 views
6

Mam dwie tabele z taką samą liczbą kolumn bez kluczy podstawowych (wiem, to nie moja wina). Teraz muszę usunąć wszystkie wiersze z tabeli A, które istnieją w tabeli B (są równe, każda z 30 kolumnami).DELETE WITH INTERSECT

Najbardziej natychmiastowy sposób, w jaki myślałem, to zrobić INNER JOIN i rozwiązać mój problem. Ale warunki zapisu dla wszystkich kolumn (martwienie się o NULL) nie jest eleganckie (może dlatego, że moje stoły też nie są eleganckie).

Chcę użyć INTERSECT. Nie wiem, jak to zrobić? To jest moje pierwsze pytanie:

próbowałem (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 

Ale wszystkie wiersze zostały usunięte z tabeli @A.

To doprowadziło mnie do drugiego pytania: dlaczego polecenie DELETE @A FROM @B usuwa wszystkie wiersze z tabeli @A?

+0

jeśli pamiętam dobrze, robi usunąć z B niczego nie ogranicza, i to normalne, że usuwa wszystko. można ograniczyć, wykonując coś takiego: USUŃ A Z B, gdzie A. wartość = B. Wartość Dodanie FROM po usunięciu jest podobne do LEWEGO DOŁĄCZENIA – Xavier

+4

Miałeś na myśli 'DELETE A', a to nie działa. Właśnie usuwasz @ CROSS JOIN (coś innego). Który usuwa wszystko, jeśli jest co najmniej jeden wiersz w czymś innym. Spójrz na plan zapytania, aby to zobaczyć. – usr

Odpowiedz

9

Spróbuj tego:

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

Usuń z @a gdzie dla każdego rekordu w @a, tam gdzie jest to mecz rekord w @a przecina się z rekordu w @b.

Jest to oparte na blog post Paula White'a przy użyciu INTERSECT do sprawdzania nierówności.

SQL Fiddle

+0

Wielkie dzięki. To jest składnia, której szukałem. Trudno jest wybrać właściwą odpowiedź, ponieważ zadałem dwa pytania. Ale najlepszą odpowiedzią na moje drugie pytanie są komentarze udzielone przez @usr. – Nizam

4

Aby odpowiedzieć na pierwsze pytanie można delete na podstawie join:

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

Drugi przypadek jest naprawdę dziwne. Pamiętam podobny przypadek tutaj i wiele skarg na to zachowanie. Spróbuję poruszyć to pytanie.

+0

Wielkie dzięki. Działa, ale chcę uniknąć sprzężenia, ponieważ liczba kolumn w moim prawdziwym przypadku. – Nizam

0
  1. Tworzenie tabeli (T) Definiowanie kluczy podstawowych
  2. wkładkę wszystkie rekordy z pod T (zakładam nie ma duplikatów w A)
  3. spróbować wstawić wszystkie rekordy z pensjonatów w T 3A. jeśli wkładka nie usuwać go z B (już nie istnieje)
  4. Opadowej T (tak naprawdę nie powinien !!!)
3

Można użyć odpowiedź Giorgi, aby usunąć wiersze, których potrzebujesz.

Jeśli chodzi o pytanie, dlaczego wszystkie wiersze zostały usunięte, to dlatego, że nie ma warunków ograniczających. Twoja klauzula FROM pobiera tabelę do przetworzenia, ale nie ma klauzuli WHERE, aby zapobiec usunięciu niektórych wierszy z @A.

+0

Posiadanie tabeli do przetworzenia jest niewystarczające http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1/1896 –

+0

Należy również rozważyć użycie opcji EXCEPT zamiast INTERSECT, jeśli ostatecznym celem jest usunięcie z tylko jednej z dwóch porównywanych tabel. Logika i kroki byłyby prostsze. –

+0

@MartinSmith - w twojej próbce @A jest opróżniany (usunięty ...). Prawdą jest, że jeśli klauzula 'FROM' jest pusta, nic nie jest usuwane, ale co do tego, co wydarzyło się w OP (gdzie klauzula' FROM' NIE jest pusta ...), powód jest taki, jak napisałem. – Amit

0

Odpowiedź Giorgi wyraźnie porównuje wszystkie kolumny, których chciałeś uniknąć. Możliwe jest napisanie kodu, który nie wyświetla jawnie wszystkich kolumn. tworzy zestaw wyników, którego potrzebujesz, ale nie wiem, jak dobrze wykorzystać ten zestaw wyników do DELETE oryginalnych wierszy od A bez klucza podstawowego. Zatem poniższe rozwiązanie zapisuje ten wynik pośredniczący w tabeli tymczasowej przy użyciu SELECT * INTO. Następnie usuwa wszystko z A i kopiuje wynik tymczasowy do A. Zawiń w transakcji.

-- 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; 

wynik ustawić

value username 
1  User 1 
3  User 3 

Dobrą stroną tego rozwiązania jest to, że używa * zamiast pełnej listy kolumn. Oczywiście możesz również jawnie wyświetlić wszystkie kolumny.Wciąż będzie łatwiej pisać i obsługiwać niż pisać porównania wszystkich kolumn i dbać o możliwe NULL.