El EXCEPT
operador se introdujo en SQL Server 2005, pero ¿cuál es la diferencia entre NOT IN
y EXCEPT
?
¿Hace lo mismo? Me gustaría una explicación simple con un ejemplo.
El EXCEPT
operador se introdujo en SQL Server 2005, pero ¿cuál es la diferencia entre NOT IN
y EXCEPT
?
¿Hace lo mismo? Me gustaría una explicación simple con un ejemplo.
Respuestas:
Hay dos diferencias clave entre EXCEPT
y NOT IN
.
EXCEPT
filtra los DISTINCT
valores de la tabla de la izquierda que no aparecen en la tabla de la derecha. Es esencialmente lo mismo que hacer un NOT EXISTS
con una DISTINCT
cláusula.
También espera que las dos tablas (o subconjunto de columnas de las tablas) tengan el mismo número de columnas en el lado izquierdo y derecho de la consulta
Por ejemplo, no puedes hacer:
SELECT ID, Name FROM TableA
EXCEPT
SELECT ID FROM TableB
Esto provocaría el error:
Todas las consultas combinadas con un operador UNION, INTERSECT o EXCEPT deben tener el mismo número de expresiones en sus listas de destino.
NOT IN
no filtra los DISTINCT
valores y devuelve todos los valores de la tabla de la izquierda que no aparecen en la tabla de la derecha.
NOT IN
requiere que compare una sola columna de una tabla con una sola columna de otra tabla o subconsulta.
Por ejemplo, si su subconsulta debía devolver varias columnas:
SELECT * FROM TableA AS nc
WHERE ID NOT IN (SELECT ID, Name FROM TableB AS ec)
Obtendría el siguiente error:
Solo se puede especificar una expresión en la lista de selección cuando la subconsulta no se introduce con EXISTS.
Sin embargo, si la tabla de la derecha contiene a NULL
en los valores que se filtran NOT IN
, se devuelve un conjunto de resultados vacío, lo que puede dar resultados inesperados.
CREATE TABLE #NewCustomers (ID INT);
CREATE TABLE #ExistingCustomers (ID INT);
INSERT INTO #NewCustomers
( ID )
VALUES
(8), (9), (10), (1), (3), (8);
INSERT INTO #ExistingCustomers
( ID )
VALUES
( 1) , (2), (3), (4), (5);
-- EXCEPT filters for DISTINCT values
SELECT * FROM #NewCustomers AS nc
EXCEPT
SELECT * FROM #ExistingCustomers AS ec
-- NOT IN returns all values without filtering
SELECT * FROM #NewCustomers AS nc
WHERE ID NOT IN (SELECT ID FROM #ExistingCustomers AS ec)
De las dos consultas anteriores, EXCEPT
devuelve 3 filas de #NewCustomers
, filtrando el 1 y 3 que coinciden #ExistingCustomers
y el duplicado 8.
NOT IN
no realiza este filtrado distinto y devuelve 4 filas #NewCustomers
con el duplicado 8.
Si ahora agregamos un NULL
a la #ExistingCustomers
tabla, vemos los mismos resultados devueltos por EXCEPT
, sin embargo NOT IN
, devolverá un conjunto de resultados vacío.
INSERT INTO #ExistingCustomers
( ID )
VALUES
( NULL );
-- With NULL values in the right-hand table, EXCEPT still returns the same results as above
SELECT * FROM #NewCustomers AS nc
EXCEPT
SELECT * FROM #ExistingCustomers AS ec
-- NOT IN now returns no results
SELECT * FROM #NewCustomers AS nc
WHERE ID NOT IN (SELECT ID FROM #ExistingCustomers AS ec)
DROP TABLE #NewCustomers;
DROP TABLE #ExistingCustomers;
En lugar de eso NOT IN
, realmente deberías mirar NOT EXISTS
y hay una buena comparación entre los dos en el blog de Gail Shaw .
Una adición al excelente comentario de Mark Sinkinson:
NOT IN requiere que compare una sola columna de una tabla con una sola columna de otra tabla o subconsulta.
En realidad, puede actuar NOT IN
con más de una columna.
Por ejemplo, esta es una consulta SQL * perfectamente legal :
SELECT E.first_name, E.last_name
FROM employees E
WHERE (E.first_name, E.last_name) NOT IN
(SELECT M.first_name, M.last_name FROM managers M)
Que volverá first_name
ylast_name
de todas las personas que son empleados, pero no son también gerentes.
*: pero la construcción aún no está implementada en SQL Server.
El NOT IN anterior falla porque debe haber una correlación entre los predicados en la consulta principal y la subconsulta. Si lo deja fuera, obtiene una subconsulta SIN CORREGIR.
SELECT * FROM TableA AS nc WHERE ID NOT IN (SELECT ID, Name FROM TableB AS ec donde nc.ID = ec.ID)
EXCEPTO es mejor y manejará cualquier fila nula sin usar predicados IS NULL / IS NOT NULL.