SQL - Définir la différence et obtenir des champs qui ne font pas partie de la différence
-
29-10-2019 - |
Question
J'ai une requête qui exécute essentiellement quelque chose comme ceci:
select a, b, c
from tab
where tab.state = 'A'
minus
select a, b, c
from tab
where tab.state = 'B'
Dans cet exemple, a
, b
et c
sont les champs clés de cette table. state
fait également partie de la clé, et j'essaie de trouver des situations où il y a un enregistrement dans l'état A et non dans l'état B.Il y a un autre champ (pas dans la clé) sur lequel je voudrais signaler, value
, qui peut être différent pour le même enregistrement dans différents états. Exemple:
a b c state value --------------------- 1 1 1 A 12 1 2 2 A 1002 1 3 9 A 43 1 1 1 B 17.34 1 2 2 B 1002
Dans ce cas, je suis intéressé par la ligne dont la clé est 1,3,9
où state est A. J'aimerais aussi obtenir la valeur de la colonne value
, mais si j'essaye:
select a, b, c, value
from tab
where tab.state = 'A'
minus
select a, b, c, value
from tab
where tab.state = 'B'
Ce que je recevrais, ce serait deux lignes:
a b c value ---------------- 1 1 1 12 1 3 9 43
Fondamentalement, je veux avoir value
dans le jeu de résultats, mais ne pas participer au minus
. J'ai l'impression de manquer quelque chose d'évident ici, mais peut-être que je suis juste trop fatigué pour l'obtenir ...;)
La solution
La manière la plus évidente de procéder est la suivante:
select a, b, c, value
from tab
where tab.state = 'A' and not exists (
select 1 -- let the optimizer do its thing
from tab ti
where tab.state = 'B' and ti.a=tab.a and ti.b=tab.b and ti.c=tab.c)
J'ajouterais même un distinct
dans la requête externe si les données peuvent avoir des doubles.
Autres conseils
You can join all rows where state = 'A'
with the matching ones with state = 'B'
...
SELECT t1.a, t1.b, t1.c, t1.value, t2.value v2
FROM (SELECT a, b, c, value FROM tab WHERE state = 'A') t1
LEFT JOIN (SELECT a, b, c, value FROM tab WHERE state = 'B') t2
ON t1.a = t2.a AND t1.b = t2.b AND t1.c = t2.c
...and then pick the rows where there were no match:
SELECT a, b, c, value
FROM ( /* previous query */ )
WHERE v2 IS NULL
SELECT a,
b,
c,
value
FROM tab tab1
INNER JOIN
(SELECT a, b, c FROM tab WHERE tab.state = 'A'
MINUS
SELECT a, b, c FROM tab WHERE tab.state = 'B'
) tab2
ON tab1.a = tab2.a
AND tab1.b = tab2.b
AND tab1.c = tab2.c
I believe above code would do the trick.