2015-06-23 16 views
6

Ho due tabelle con lo stesso numero di colonne senza chiavi primarie (lo so, non è colpa mia). Ora ho bisogno di cancellare tutte le righe dalla tabella A che esiste nella tabella B (sono uguali, ognuna con 30 colonne).CANCELLA CON INTERSETTO

Il modo più immediato che ho pensato è di fare un INNER JOIN e risolvere il mio problema. Ma, scrivere le condizioni per tutte le colonne (preoccuparsi di NULL) non è elegante (forse anche i miei tavoli non sono eleganti).

Desidero utilizzare INTERSECT. Non so come farlo? Questa è la mia prima domanda:

ho cercato (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 

Ma tutte le righe sono stati cancellati dalla tabella @A.

Questo mi ha portato alla seconda domanda: perché il comando DELETE @A FROM @B elimina tutte le righe dalla tabella @A?

+0

se ricordo bene, facendo DELETE FROM A B non limita nulla, e questo normale che cancella tutto. si può limitare facendo qualcosa del tipo: CANCELLA A DA B dove A.valore = B.Valore L'aggiunta di un FROM dopo un'eliminazione è simile a un SINISTRA JOIN – Xavier

+4

Intendevi 'DELETE A' e quello non funziona. In questo momento stai cancellando @A CROSS JOIN (qualcos'altro). Che cancella tutto se c'è almeno una riga in qualcos'altro. Guarda il piano di query per vedere questo. – usr

risposta

9

Prova questa:

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

Cancella dalla @A dove, per ogni record @A, c'è una partita in cui il record nel @A interseca con un record in @B.

Questo è basato su di Paul White utilizzando INTERSECT per il controllo della disuguaglianza.

SQL Fiddle

+0

Grazie mille. Questa è la sintassi che stavo cercando. È difficile scegliere la risposta giusta da quando ho fatto due domande. Ma la risposta migliore per la mia seconda domanda è sui commenti forniti da @usr. – Nizam

4

Per rispondere alla tua prima domanda si può eliminare in base agli join:

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

Il secondo caso è davvero strano. Ricordo casi simili qui e molte lamentele su questo comportamento. Proverò a fingere quella domanda.

+0

Grazie mille. Funziona ma sto cercando di evitare il join perché il numero di colonne nel mio caso reale. – Nizam

0
  1. Creare una tabella (T) che definisce le chiavi primarie
  2. inserto tutti i record da A in T (I assumerà non ci sono duplicati in A)
  3. tenta di inserire tutti i record da B in T 3A. se inserimento non riesce eliminazione dal B (già esistente)
  4. goccia T (davvero non dovrebbe !!!)
3

È possibile utilizzare la risposta di Giorgi per eliminare le righe si ha bisogno.

Per quanto riguarda la domanda relativa al motivo per cui tutte le righe sono state eliminate, questo perché non vi è alcuna condizione di limitazione. La tua clausola FROM riceve una tabella da elaborare, ma non esiste una clausola WHERE per impedire l'eliminazione di determinate righe da @A.

+0

Avere una tabella da elaborare non è sufficiente http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1/1896 –

+0

Considerare anche l'utilizzo di EXCEPT anziché di INTERSECT se il proprio obiettivo finale è quello di eliminare solo una delle due tabelle confrontate. La logica e i passaggi sarebbero più semplici. –

+0

@MartinSmith - nel tuo esempio @A viene svuotato (eliminato ...). È vero che se la clausola 'FROM' è vuota, nulla viene eliminato, ma per quanto riguarda l'OP (dove la clausola' FROM' NON è vuota ...), la ragione è come ho affermato. – Amit

0

La risposta di Giorgi confronta in modo esplicito tutte le colonne, che si desidera evitare. È possibile scrivere codice che non elenca tutte le colonne esplicitamente. EXCEPT produce il set di risultati necessario, ma non conosco un buon modo per utilizzare questo set di risultati su DELETE righe originali da A senza chiave primaria. Quindi, la soluzione seguente salva questo risultato intermedio in una tabella temporanea utilizzando SELECT * INTO. Quindi elimina tutto da A e copia il risultato temporaneo in A. Avvolgilo in una transazione.

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

risultato impostato

value username 
1  User 1 
3  User 3 

Il lato di questa soluzione è che utilizza * anziché la lista completa di colonne. Naturalmente, puoi anche elencare tutte le colonne in modo esplicito.Sarà ancora più facile scrivere e gestire, piuttosto che scrivere confronti di tutte le colonne e occuparsi dei possibili NULL.