17 abril 2026

El peligro del "NOT IN" y por qué deberías usar "NOT EXISTS" hoy mismo

Si trabajas con bases de datos, tarde o temprano te enfrentarás a esta pregunta: ¿Cómo obtengo las filas de una tabla que no aparecen en otra? Por ejemplo, marcas que no tienen ningún artículo asociado. A simple vista, estas dos consultas parecen gemelas:

Opción A (NOT IN):
SELECT marca FROM marca
WHERE marca NOT IN (SELECT marca FROM articulo);

Opción B (NOT EXISTS):
SELECT marca FROM marca m
WHERE NOT EXISTS (SELECT 1 FROM articulo a WHERE a.marca = m.marca)

Lo dicho, podríamos pensar que estas dos consultas hacen exactamente lo mismo, e incluso opinar que la primera es muchísmo más entendible que la segunda. Sin embargo, en SQL, son como dos caminos aparentemente iguales pero que te pueden llevar a resultados totalmente distintos cuando aparecen los "temidos" nulos.

Pensemos en NOT IN. Parece intuitivo: "dame las marcas que no estén en esta lista". El problema es que SQL utiliza la lógica trivalente, donde las cosas no solo son verdaderas o falsas, sino que existe una tercera opción: lo desconocido. Si en tu tabla de artículos hay una sola fila con una marca NULL, el NOT IN "se rompe". SQL se preguntará: "¿Es mi marca diferente de Apple? Sí. ¿Es diferente de este valor que no conozco (NULL)? No lo sé". Como SQL necesita una certeza para mostrar una fila, ante la duda de un nulo decide ocultarla. El resultado es que tu consulta se queda vacía por culpa de una sola fila con datos desconocidos.

Aquí es donde el NOT EXISTS te salva la vida. A diferencia del anterior, este no se pierde en adivinanzas. Simplemente se asoma a la otra tabla y busca una coincidencia: "¿Hay alguna fila aquí que sea 'Samsung'?". Los nulos de la otra tabla nunca responderán que "sí" a esa pregunta, por lo que no bloquean el proceso.

Además, el NOT EXISTS es mucho más "espabilado" y eficiente. En cuanto encuentra la primera fila que coincide, deja de buscar y pasa a la siguiente. Es lo que llamamos parada temprana, y es lo que permite que tu base de datos vuele aunque tengas millones de filas. Al final, usar NOT EXISTS no es solo cuestión de rendimiento, es la única forma de garantizar un resultado correcto y dormir tranquilo aunque tus datos tengan huecos.

  • El problema de los nulos: una regla estándar (ANSI SQL)

El comportamiento del NOT IN con nulos no es un fallo de un programa concreto, sino una regla del estándar SQL (lógica trivalente). Por tanto, ocurre en todos los motores principales, SQL Server, Oracle, PostgreSQL, MariaDB, MySQL y SQLite se comportan igual: si hay un NULL en la lista del NOT IN, el resultado será vacío.

NOT EXISTS es siempre la solución segura en cualquiera de estos sistemas. 

  • El rendimiento: aquí hay matices

Para que un NOT IN sea seguro, la mayoría de las veces, tenemos que realizar comprobaciones extra para asegurar que no hay ningún NULL escondido que pueda invalidar todo el resultado.

EXISTS, al basarse en una comparación directa entre filas (a.marca = m.marca), permite que el motor use índices, si existen, de forma mucho más agresiva y directa, sin preocuparse por la ambigüedad de los valores desconocidos.

Además, si la lista de la subconsulta es enorme, IN puede llegar a saturar la memoria temporal (tempdb o work_mem) del servidor. EXISTS es mucho más ligero en memoria porque no necesita almacenar listas de valores; solo necesita saber si "hay algo" o "no hay nada". 

Aunque EXISTS suele ser el rey de la eficiencia, los motores modernos han evolucionado. En motores "inteligentes" (SQL Server, Oracle, PostgreSQL) sus optimizadores son ya tan avanzados que, a veces, detectan que estás usando un IN y lo transforman internamente en un semi-join (una operación similar a EXISTS) para que no pierdas velocidad. O directamente en un EXISTS, lo que pasa es que no siempre pueden hacerlo, lo que obliga al motor a elegir un camino más lento y conservador para no equivocarse.

Históricamente, EXISTS en MariaDB/MySQL ha tenido una ventaja mucho más clara sobre IN en versiones antiguas, aunque en las versiones más recientes (8.0+) la brecha se ha cerrado bastante.

La regla de oro: aunque el motor sea listo, EXISTS nunca será más lento que IN, pero IN sí puede ser mucho más lento que EXISTS si el optimizador no logra transformarlo bien. 

Puedes decir con total tranquilidad que NOT EXISTS es "lo profesional", el único que te garantiza un resultado correcto en cualquier base de datos del mundo, sin importar si hay nulos o si el motor es más o menos moderno. IN / NOT IN se queda para comparaciones básicas con constantes: marca IN ('Samsung', 'Apple', 'Xiaomi'). Vale, sí, EXISTS exige un poco más de esfuerzo tan solo en entender qué hemos escrito, pero queremos ser "pro", ¿no?

No hay comentarios:

Publicar un comentario

Los comentarios son revisados antes de su publicación en prevención de usos incorrectos (spam o alusiones insultantes a terceros). Por eso, tu comentario puede tardar un poco, no mucho, en ser visible.