Operazione di differenza simmetrica in Transact-SQL?


10

Ho sempre saputo UNIONdell'operatore in SQL, ma solo di recente ho scoperto che c'erano altri operatori di set INTERSECTe EXCEPT. Non sono stato in grado di trovare un operatore che esegua il quarto grande operatore, la differenza simmetrica (ad esempio il contrario di INTERSECT.)

Sembra che riesca a ottenere l'output desiderato usando qualcosa di simile

SELECT Field FROM A UNION SELECT Field FROM B 
EXCEPT
SELECT Field FROM A INTERSECT SELECT Field FROM B

(supponendo che ho ottenuto la precedenza giusta), o facendo un anti-full-join:

SELECT A.Field, B.Field
FROM A
FULL JOIN B ON B.Id = A.Id
WHERE B.Id IS NULL OR A.Id IS NULL

Ma entrambi sembrano query piuttosto intense, soprattutto rispetto alle altre tre operazioni di base. Esiste un'operazione di differenza simmetrica in SQL e non riesco proprio a trovarla nella documentazione? O esiste un modo "canonico" per implementarlo in T-SQL?


2
(a EXCEPT b) UNION ALL (b EXCEPT a);potrebbe essere più efficiente.
ypercubeᵀᴹ

@ypercube che esegue 3 join o operazioni simili a join. Non riesco a pensare a nessun motivo per cui questo potrebbe essere più efficiente di un full join.
usr

@usr in realtà sono 2 operazioni simili a join, non 3. Union All è un'operazione molto meno costosa. Sono d'accordo che FULL JOINpotrebbe essere più efficiente. I test possono rivelare qual è il migliore. E, naturalmente, se sono necessarie più / diverse colonne per ogni tabella, la mia soluzione non è facilmente espandibile.
ypercubeᵀᴹ

Risposte:


3

Tutti gli operatori impostati vengono tradotti in join o operatori simili a join. Puoi testimoniarlo nel piano delle query.

Per questo motivo il full outer join che hai lì è il più efficiente che puoi fare. Ignorando, ovviamente, la situazione, si spera, rara in cui l'ottimizzatore sceglie un piano negativo e una riscrittura sembra funzionare meglio per fortuna. Questo può sempre succedere.


Questo ha senso, tuttavia, il motivo per cui mi piace usare gli operatori set è perché non creano "colonne extra" ... Posso fare cose del genere SELECT Id FROM A WHERE <stuff> EXCEPT Select Id FROM A WHERE <other stuff>e ottenere un unico elenco di Id. Non riesco a capire come farlo con un join completo ... Devo solo gestire due serie di Idcolonne e unirle di nuovo insieme?
KutuluMike,

Si può dire ISNULL(a.Col, b.Col) AS Col. Avvolgilo in una tabella derivata o CTE e puoi utilizzare il set di risultati "piegato" per ulteriori operazioni su di esso. (A proposito, sono d'accordo che l'operatore di differenza simmetrica dovrebbe esistere.)
usr

2
@MichaelEdenfield D'altra parte, che dire di quando vuoi altre colonne che non vengono utilizzate nel confronto / distinto? Puoi farlo con un join ma non con intersect / tranne. Quindi ho potuto vedere in alcuni casi che la variazione finisce per diventare molto più complessa nel lungo periodo.
Aaron Bertrand

2

Penso che questa sia una soluzione abbastanza buona. Usa un'unione tutta tra due selezioni con le sottoquery Non esiste. Meglio che combinare Union ed Tranne perché è possibile includere campi che non corrispondono nella proiezione.

SELECT  'A' TableName, FileType FROM KFX_Inventory I 
    WHERE Not Exists (Select Top 1 1 from KFX_FileType FT WHERE FT.FileType = I.FileType)
UNION ALL
SELECT  'B', FileType FROM KFX_FILEType FT 
    WHERE Not Exists (Select Top 1 1 from KFX_Inventory I WHERE FT.FileType = I.FileType)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.