Opération de différence symétrique dans Transact-SQL?

10

J'ai toujours connu l' UNIONopérateur en SQL, mais je n'ai découvert que récemment qu'il y avait d'autres opérateurs d'ensemble, INTERSECTet EXCEPT. Je n'ai pas pu trouver d'opérateur qui fasse le quatrième grand opérateur d'ensemble, la différence symétrique (par exemple l'opposé de INTERSECT.)

Il semble que je puisse obtenir la sortie souhaitée en utilisant quelque chose comme

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

(en supposant que j'ai la priorité), ou en faisant une anti-jointure complète:

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

Mais les deux ressemblent à des requêtes assez intensives, en particulier par rapport aux trois autres opérations de base. Existe-t-il une opération de différence symétrique dans SQL et je ne la trouve tout simplement pas dans la documentation? Ou existe-t-il un moyen "canonique" de l'implémenter dans T-SQL?

KutuluMike
la source
2
(a EXCEPT b) UNION ALL (b EXCEPT a);pourrait être plus efficace.
ypercubeᵀᴹ
@ypercube qui effectue 3 jointures ou opérations de type jointure. Je ne vois aucune raison pour laquelle cela pourrait être plus efficace qu'une jointure complète.
usr
@usr c'est en fait 2 opérations de type jointure, pas 3. Union All est une opération beaucoup moins coûteuse. Je conviens que cela FULL JOINpourrait être plus efficace. Les tests peuvent révéler ce qui est le mieux. Et bien sûr, si des colonnes supplémentaires / différentes de chaque table sont nécessaires, ma solution n'est pas facilement extensible.
ypercubeᵀᴹ

Réponses:

3

Tous les opérateurs définis sont traduits en jointures ou opérateurs de type jointure. Vous pouvez en témoigner dans le plan de requête.

Pour cette raison, la jointure externe complète que vous avez est la plus efficace possible. Sans tenir compte, bien sûr, de la situation, espérons-le, rare dans laquelle l'optimiseur choisit un mauvais plan et une réécriture se produit mieux par chance. Cela peut toujours arriver.

usr
la source
Cela a du sens, cependant, la raison pour laquelle j'aime utiliser les opérateurs set est parce qu'ils ne créent pas de "colonnes supplémentaires" ... Je peux faire des choses comme SELECT Id FROM A WHERE <stuff> EXCEPT Select Id FROM A WHERE <other stuff>et obtenir une seule liste de Id. Je ne peux pas comprendre comment faire cela avec une jointure complète ... ai-je juste besoin de gérer deux ensembles de Idcolonnes et de les réunir à nouveau moi-même?
KutuluMike
Vous pouvez dire ISNULL(a.Col, b.Col) AS Col. Enveloppez cela dans une table dérivée ou CTE et vous pouvez utiliser le jeu de résultats "plié" pour une opération ultérieure. (Btw, je suis d'accord que l'opérateur de différence symétrique devrait exister.)
usr
2
@MichaelEdenfield D'un autre côté, qu'en est-il lorsque vous voulez d' autres colonnes qui ne sont pas utilisées dans la comparaison / distinction? Vous pouvez le faire avec une jointure mais pas avec intersection / sauf. J'ai donc pu voir dans certains cas que la variation finissait par devenir beaucoup plus complexe à long terme.
Aaron Bertrand
2

Je pense que c'est une assez bonne solution. Il utilise une union entre deux sélections avec des sous-requêtes Not Exists. Mieux que de combiner Union et Except car vous pouvez inclure des champs qui ne correspondent pas dans la projection.

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)
Darrel Lee
la source