Se puede concluir de las respuestas aquí que NOT IN (subquery)
no maneja nulos correctamente y debe evitarse a favor NOT EXISTS
. Sin embargo, tal conclusión puede ser prematura. En el siguiente escenario, acreditado a Chris Date (Database Programming and Design, Vol 2 No 9, septiembre de 1989), es NOT IN
que maneja los valores nulos correctamente y devuelve el resultado correcto, en lugar de NOT EXISTS
.
Considere una tabla sp
para representar proveedores ( sno
) que se sabe que suministran piezas ( pno
) en cantidad ( qty
). La tabla actualmente contiene los siguientes valores:
VALUES ('S1', 'P1', NULL),
('S2', 'P1', 200),
('S3', 'P1', 1000)
Tenga en cuenta que la cantidad es anulable, es decir, para poder registrar el hecho de que se sabe que un proveedor suministra piezas, incluso si no se sabe en qué cantidad.
La tarea es encontrar los proveedores que son conocidos con el número de parte de suministro 'P1' pero no en cantidades de 1000.
Los siguientes usos solo NOT IN
identifican correctamente al proveedor 'S2':
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1', NULL ),
( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT DISTINCT spx.sno
FROM sp spx
WHERE spx.pno = 'P1'
AND 1000 NOT IN (
SELECT spy.qty
FROM sp spy
WHERE spy.sno = spx.sno
AND spy.pno = 'P1'
);
Sin embargo, la consulta a continuación utiliza la misma estructura general pero NOT EXISTS
incluye incorrectamente al proveedor 'S1' en el resultado (es decir, para el cual la cantidad es nula):
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1', NULL ),
( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT DISTINCT spx.sno
FROM sp spx
WHERE spx.pno = 'P1'
AND NOT EXISTS (
SELECT *
FROM sp spy
WHERE spy.sno = spx.sno
AND spy.pno = 'P1'
AND spy.qty = 1000
);
¡Entonces NOT EXISTS
no es la bala de plata que pudo haber aparecido!
Por supuesto, la fuente del problema es la presencia de nulos, por lo tanto, la solución 'real' es eliminar esos nulos.
Esto se puede lograr (entre otros diseños posibles) usando dos tablas:
sp
proveedores conocidos por suministrar piezas
spq
proveedores conocidos por suministrar piezas en cantidades conocidas
señalando que probablemente debería haber una restricción de clave externa donde las spq
referencias sp
.
El resultado se puede obtener utilizando el operador relacional 'menos' (que es la EXCEPT
palabra clave en SQL estándar), por ejemplo
WITH sp AS
( SELECT *
FROM ( VALUES ( 'S1', 'P1' ),
( 'S2', 'P1' ),
( 'S3', 'P1' ) )
AS T ( sno, pno )
),
spq AS
( SELECT *
FROM ( VALUES ( 'S2', 'P1', 200 ),
( 'S3', 'P1', 1000 ) )
AS T ( sno, pno, qty )
)
SELECT sno
FROM spq
WHERE pno = 'P1'
EXCEPT
SELECT sno
FROM spq
WHERE pno = 'P1'
AND qty = 1000;
NOT IN
a una serie de<> and
cambios el comportamiento semántico de no estar en este conjunto a otra cosa?