2010-07-22 10 views
32

Ho due tabelle (procedure e Timeentries), che sono collegati da una chiave esterna (TimeEntries.TaskID riferimento Tasks.ID)modo elegante per eliminare le righe che non si fa riferimento da un altro tavolo

Ora vorrei eliminare tutte le righe da Attività che non sono referenziate dalla tabella TimeEntries. Ho pensato che questo dovrebbe funzionare:

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

ma colpisce 0 righe, anche se ci sono un sacco di righe senza riferimenti nella tabella Tasks.

Quale potrebbe essere il problema qui? Ovviamente potrei scrivere un SP che itera tutte le righe, ma sembra che questo potrebbe essere fatto in un solo liner.

Immagino che questo sia uno di quegli errori di underflow di sleeptime. Per favore aiuto!

+2

Ottiene i risultati previsti se si esegue la subquery SELECT da sola? – JNK

+0

@ J-N-K: sì, l'ho fatto. –

risposta

49

C'è un noto trucchetto per not in. In sostanza, id not in (1,2,3) è una scorciatoia per:

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

Ora, se il vostro tavolo TimeEntries contiene ogni riga con una TaskID di null, il not in si traduce in:

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

Il risultato di un confronto con null è sempre unknown . Poiché unknown non è vero in SQL, la clausola where esclude tutte le righe e non si elimina nulla.

Una soluzione semplice è un ulteriore clausola where nella subquery:

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

Hai un suggerimento su come aggirare questo? – DOK

+0

@DOK: certo, un modo aggiunto alla risposta. SQLMenace ha pubblicato un'altra buona risposta – Andomar

+0

. Molte grazie! –

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

SQL Above dovrebbe eliminare tutte le righe dalle attività in cui Task.ID non esiste nella tabella delle voci di tempo. Lo eseguirò come istruzione select prima di test :)

9

Dato che si sta utilizzando SQL 2008, è possibile utilizzare la nuova sintassi di fusione nifty.

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

+1: un po 'eccessivo in questo caso, ma è certamente utile conoscere il comando di unione. Non sapevo nemmeno che esistesse qualcosa del genere. –

+1

Perché pensi che sia eccessivo? È praticamente fatto per compiti come questo (abbinare le righe tra le tabelle). – JohnFx

+1

E le sue prestazioni rispetto alle soluzioni precedenti? Ha qualche vantaggio o è questo il modo supportato per fare tali azioni? Thx –

3

So che questo è vecchio, ma mi chiedo perché nessuno ha menzionato una query di eliminazione come descritto here. Quindi, per riferimento:

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

Questa sintassi non è compatibile ISO, quindi funzionerà solo con T-SQL.

Problemi correlati