sqlsql-serverrendimientobases-de-datos
NOT IN y NOT EXISTS parecen hacer lo mismo, pero no es asi. Cuando la subquery contiene un NULL, NOT IN falla silenciosamente y devuelve cero filas sin ningun error. En este post vamos a ver por que ocurre y cual conviene usar.El escenario
Tienes dos tablas:
Users y Orders. Quieres obtener los usuarios que no han realizado ningun pedido. Las dos formas mas comunes de resolverlo son estas:sql
-- Opcion 1: NOT IN
-- Compara el id contra toda la lista de userId
SELECT *
FROM Users
WHERE id NOT IN (
SELECT userId FROM Orders
);sql
-- Opcion 2: NOT EXISTS
-- Evalua fila por fila si existe correspondencia
SELECT *
FROM Users u
WHERE NOT EXISTS (
SELECT 1 FROM Orders o
WHERE o.userId = u.id
);A primera vista ambas consultas parecen equivalentes, y en muchos casos lo son. Pero hay un escenario donde
NOT IN se rompe silenciosamente.La trampa del NULL
En SQL, comparar cualquier valor con
NULL devuelve UNKNOWN, no TRUE ni FALSE. El problema es que NOT IN necesita que todas las comparaciones sean FALSE para incluir una fila. Si la subquery devuelve aunque sea un solo NULL, cada comparacion produce UNKNOWN y la consulta entera devuelve cero filas para todos los registros.NOT EXISTS, en cambio, solo verifica si la subquery devuelve filas o no. Los valores NULL simplemente no hacen match con ningun registro de la tabla exterior (porque NULL = algo es UNKNOWN, no TRUE), y no afectan al resto de la evaluacion. Por eso NOT EXISTS es inmune a este problema.Cuidado con NOT IN
Si tu subquery puede devolver NULL, NOT IN va a devolver cero filas sin ningun aviso.
Cual usar?
- Por defecto, usa NOT EXISTS. Es seguro frente a
NULLy el optimizador de SQL Server lo maneja muy bien. - NOT IN esta bien solo si estas seguro de que no hay
NULLen la subquery y la lista de valores es pequena.
Si tienes dudas, usa
NOT EXISTS. Es la opcion que menos sorpresas te va a dar.Para terminar
Un solo
NULL en la subquery puede hacer que NOT IN falle sin avisarte; NOT EXISTS no tiene ese problema.