NOT IN vs NOT EXISTS en SQL Server: no son lo mismo

Jorge SaavedraJorge Saavedra
·14 de febrero, 2025·3 min de lectura
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 NULL y el optimizador de SQL Server lo maneja muy bien.
  • NOT IN esta bien solo si estas seguro de que no hay NULL en 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.

Posts que podrian interesarte