SQL - Imposta la differenza e ottenere campi che non fanno parte della differenza
-
29-10-2019 - |
Domanda
Ho una domanda che praticamente esegue qualcosa di simile:
select a, b, c
from tab
where tab.state = 'A'
minus
select a, b, c
from tab
where tab.state = 'B'
In questo esempio, a
, b
, e c
sono i campi chiave di questa tabella. state
è anche una parte della chiave e sto cercando di trovare situazioni in cui c'è un record nello stato A e non nello stato B. C'è un altro campo (non nella chiave) su cui mi piacerebbe riferire, value
, potrebbe essere diverso per lo stesso record in stati diversi. Esempio:
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
In questo caso, sono interessato alla riga di cui è la chiave 1,3,9
dove lo stato è A. Mi piacerebbe anche ottenere il valore del value
colonna, ma se provo:
select a, b, c, value
from tab
where tab.state = 'A'
minus
select a, b, c, value
from tab
where tab.state = 'B'
Quello che vorrei restituire è due righe:
a b c value ---------------- 1 1 1 12 1 3 9 43
Fondamentalmente, voglio avere value
nel set di risultati, ma non partecipa al minus
. Sento che mi manca qualcosa di ovvio qui, ma forse sono troppo stanco per ottenerlo ...;)
Soluzione
Il modo ovvio per farlo è così:
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)
Aggiungerei anche un distinct
Nella query esterna se i dati possono avere doppi.
Altri suggerimenti
Puoi unirti a tutte le righe dove state = 'A'
con quelli abbinati con 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
... e poi scegli le righe dove non c'erano corrispondenze:
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
Credo che il codice sopra farebbe il trucco.