SQL: establezca la diferencia y obtenga campos que no son parte de la diferencia
-
29-10-2019 - |
Pregunta
Tengo una consulta que básicamente realiza algo como esto:
select a, b, c
from tab
where tab.state = 'A'
minus
select a, b, c
from tab
where tab.state = 'B'
En este ejemplo, a
, b
, y c
son los campos clave de esta tabla. state
También es parte de la clave, y estoy tratando de encontrar situaciones en las que haya un registro en el estado A y no en el estado B. Hay otro campo (no en la clave) en el que me gustaría informar, value
, eso podría ser diferente para el mismo registro en diferentes estados. Ejemplo:
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
En este caso, estoy interesado en la fila cuya clave es 1,3,9
Donde el estado está A. También me gustaría obtener el valor del value
columna, pero si lo intento:
select a, b, c, value
from tab
where tab.state = 'A'
minus
select a, b, c, value
from tab
where tab.state = 'B'
Lo que me devolverían son dos filas:
a b c value ---------------- 1 1 1 12 1 3 9 43
Básicamente, quiero tener value
en el conjunto de resultados, pero no participar en el minus
. Siento que me estoy perdiendo algo obvio aquí, pero tal vez estoy demasiado cansado para conseguirlo ...;)
Solución
La forma obvia de hacer esto es así:
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)
Incluso agregaría un distinct
en la consulta externa si los datos pueden tener dobles.
Otros consejos
Puedes unirte a todas las filas donde state = 'A'
con los coincidentes 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
... y luego elige las filas donde no hubo partido:
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
Creo que el código anterior haría el truco.