2010-07-22 12 views
32

Mam dwie tabele (zadania i Timeentries), które są połączone przez klucz obcy (TimeEntries.TaskID odwołuje Tasks.ID)elegancki sposób, aby usunąć wiersze, które nie są wymienione przez inne tabeli

Teraz chciałbym aby usunąć wszystkie wiersze z zadań, które nie są przywoływane przez tabelę TimeEntries. Myślałem, że to powinno działać:

DELETE FROM Tasks WHERE ID not IN (SELECT TaskID FROM TimeEntries) 

Ale to wpływa 0 wierszy, choć istnieje wiele przypisań wierszy w tabeli Zadania.

Jaki może być problem? Oczywiście mógłbym napisać SP, który iteruje wszystkie wiersze, ale wygląda na to, że można to zrobić w jednej linijce.

Domyślam się, że jest to jeden z tych błędów niedoboru w czasie sceptime. Proszę pomóż!

+2

Czy uzyskasz oczekiwane rezultaty, jeśli samodzielnie uruchomisz podkwerendę SELECT? – JNK

+0

@ J-N-K: Tak, zrobiłem. –

Odpowiedz

49

Jest jedna osławiona wiadomość dla not in. Zasadniczo id not in (1,2,3) jest skrótem dla:

id <> 1 and id <> 2 and id <> 3 

Teraz jeśli tabela TimeEntries zawiera dowolny wiersz z TaskID z null The not in przekłada się:

ID <> null and ID <> 1 and ID <> 2 AND ... 

Wynikiem porównania z null jest zawsze unknown . Ponieważ unknown nie jest prawdą w SQL, klauzula where odfiltrowuje wszystkie wiersze, a na końcu nic nie usuniesz.

łatwo naprawić to dodatkowy gdzie klauzula podkwerendzie:

DELETE FROM Tasks 
WHERE ID not IN 
     (
     SELECT TaskID 
     FROM TimeEntries 
     WHERE TaskID is not null 
     ) 
+0

Czy masz sugestię, jak sobie z tym poradzić? – DOK

+0

@DOK: Oczywiście, jeden sposób dodany do odpowiedzi. SQLMenace opublikował kolejną dobrą odpowiedź: – Andomar

+0

Doskonała odpowiedź. Wielkie dzięki! –

3
Delete FROM Tasks 
     WHERE not Exists 
      (SELECT 'X' FROM TimeEntries where TimeEntries.TaskID = Tasks.ID) 

Powyższy SQL powinien usunąć wszystkie wiersze z zadań, w których Task.ID nie istnieje w tabeli wpisów czasu. Uruchomiłbym to jako pierwsze zdanie do przetestowania :)

9

Odkąd używasz SQL 2008, możesz użyć sprytnej nowej składni korespondencji seryjnej.

MERGE Tasks AS target 
USING TimeEntries as Source ON (Target.TaskID=Source.TaskID) 
WHEN NOT MATCHED BY Source THEN DELETE; 
+1

+1: W tym przypadku nieco przesadzam, ale z pewnością warto wiedzieć o poleceniu scalania. Nie wiedziałem nawet, że coś takiego istnieje. –

+1

Dlaczego uważasz, że to przesada? Jest on przeznaczony dla takich zadań (dopasowywanie wierszy między tabelami). – JohnFx

+1

Co z jego wydajnością w porównaniu do poprzednich rozwiązań? Czy ma jakieś zalety, czy jest to obsługiwany sposób wykonywania takich działań? Thx –

3

wiem, że to jest stary, ale zastanawiam się, dlaczego nikt nie wspomniał o DELETE jak opisano here. Więc dla odniesienia:

DELETE FROM Tasks 
FROM Tasks LEFT OUTER JOIN 
    TimeEntries ON TimeEntries.TaskID = Tasks.ID 
WHERE TimeEntries.TaskID IS NULL; 

Ta składnia nie jest zgodna z ISO, więc będzie działać tylko dla T-SQL.

Powiązane problemy