Ir al contenido principal
¿Te ayudamos?
oposiciones

SQL IN, NOT IN y EXISTS: 8 ejercicios con NULL

13 min de lectura

Ocho ejercicios SQL ejecutados en SQLite: IN, NOT IN, EXISTS y NULL. Razona subconsultas correlacionadas, conjuntos vacíos y resultados paso a paso.

En estos ejercicios SQL de IN, NOT IN y EXISTS con NULL, lo decisivo es seguir el predicado que filtra cada fila. «Sin coincidencia» no siempre produce verdadero al negar una pertenencia, y una subconsulta que devuelve una fila nula no es una subconsulta vacía.

Son ocho preguntas originales con cuatro alternativas y soluciones razonadas, comprobadas en SQLite 3.53.4 con datos ficticios. Cada consulta parte de la misma inicialización y solo lee las tablas. No son preguntas oficiales ni mediciones de rendimiento de un gestor.

Inicialización de la práctica

Utiliza una base de datos de prácticas vacía y ejecuta este bloque una vez. Para reiniciar, crea otra base vacía: repetir CREATE TABLE sobre las mismas tablas produciría un error.

CREATE TABLE usuarios (
  id INTEGER PRIMARY KEY,
  nombre TEXT NOT NULL
);
CREATE TABLE compras (
  id INTEGER PRIMARY KEY,
  usuario_id INTEGER,
  estado TEXT NOT NULL
);
INSERT INTO usuarios (id, nombre) VALUES
  (1, 'Ada'), (2, 'Berta'), (3, 'Ciro'), (4, 'Duna');
INSERT INTO compras (id, usuario_id, estado) VALUES
  (101, 1, 'pagada'), (102, 1, 'pagada'),
  (103, 2, 'pagada'), (104, NULL, 'pagada'),
  (105, 3, 'anulada');
id de usuarionombre
1Ada
2Berta
3Ciro
4Duna
id de comprausuario_idestado
1011pagada
1021pagada
1032pagada
104NULLpagada
1053anulada

Los ids de ambas tablas son claves primarias. usuario_id admite NULL y no se ha declarado una clave foránea. La compra 104 no identifica un usuario concreto; la de Ciro existe aunque esté anulada. Las dos compras pagadas de Ada son filas distintas.

Cuatro comprobaciones antes de responder

Revisa la columna de la subconsulta. IN y NOT IN comparan aquí un valor escalar con una sola columna de resultados. Localiza la correlación: c.usuario_id = u.id relaciona cada compra con el usuario exterior; sin esa condición, la pregunta puede ser global.

Distingue falso y desconocido. WHERE conserva solo verdadero. En una igualdad ordinaria con NULL no se obtiene una coincidencia verdadera. Comprueba si hay cero filas o una fila nula: EXISTS se basa en la existencia de filas de estas subconsultas sencillas, no en que su columna contenga un valor no nulo.

Las reglas se contrastan con la documentación de expresiones de SQLite, apartados 8, 10 y 12 y su filtrado mediante WHERE, apartado 2.3. La referencia de subconsultas de PostgreSQL 18, apartados 9.24.1 a 9.24.3 sirve de contraste conceptual. La ejecución de esta práctica corresponde a SQLite; no se ha ejecutado PostgreSQL.

Las salidas solicitadas son secuencias de ids, ordenadas por el ORDER BY escrito en cada consulta. «Ninguna fila» significa resultado vacío, no una fila con NULL. No estudiamos comparación de filas compuestas, conversiones de tipos ni planes del optimizador.

Ejercicio 1. IN conserva coincidencias, aunque haya NULL

¿Qué ids de usuarios devuelve esta consulta con IN, en el orden indicado?

SELECT u.id
FROM usuarios AS u
WHERE u.id IN (
  SELECT c.usuario_id
  FROM compras AS c
  WHERE c.estado = 'pagada'
)
ORDER BY u.id;

A. 1, 2, 3, 4.

B. 1.

C. 1, 2.

D. 1, 2, 3.

Ver solución del ejercicio 1

Correcta: C. La salida es 1, 2. La subconsulta proporciona 1, 1, 2 y NULL. Tanto 1 como 2 encuentran una coincidencia verdadera. Las dos compras pagadas de Ada no duplican su fila exterior: se evalúa una condición sobre cada usuario.

A conserva a quienes no tienen coincidencia. B olvida la compra pagada de Berta. D incluye a Ciro, cuya compra figura como anulada. La presencia de NULL a la derecha no vuelve desconocida una coincidencia que ya es verdadera; sí importa cuando no se encuentra igualdad.

Ejercicio 2. NOT IN y el valor desconocido

Al sustituir IN por NOT IN sin cambiar la subconsulta, ¿qué ids devuelve la consulta?

SELECT u.id
FROM usuarios AS u
WHERE u.id NOT IN (
  SELECT c.usuario_id
  FROM compras AS c
  WHERE c.estado = 'pagada'
)
ORDER BY u.id;

A. Ninguna fila.

B. 3, 4.

C. 1, 2, 3, 4.

D. 4.

Ver solución del ejercicio 2

Correcta: A. La salida queda sin filas. Para 1 y 2 existe coincidencia, por lo que NOT IN es falso. Para 3 y 4 no hay igualdad, pero la comparación con el NULL de la subconsulta deja el resultado desconocido, no verdadero. WHERE no conserva ninguno de esos cuatro usuarios.

B responde como si se hubiera eliminado el nulo de la derecha. C ignora el filtro y D supone que la falta de compras de Duna basta para obtener verdadero en esta expresión. El resultado vacío no significa que todos tengan compras pagadas: describe cómo se ha evaluado el predicado escrito.

id de usuarioIN del caso 1NOT IN del caso 2Pasa WHERE en el caso 2
1VerdaderoFalsoNo
2VerdaderoFalsoNo
3DesconocidoDesconocidoNo
4DesconocidoDesconocidoNo

Ejercicio 3. Excluir NULL de la subconsulta

¿Qué ids devuelve NOT IN cuando se excluyen los NULL de la subconsulta?

SELECT u.id
FROM usuarios AS u
WHERE u.id NOT IN (
  SELECT c.usuario_id
  FROM compras AS c
  WHERE c.estado = 'pagada'
    AND c.usuario_id IS NOT NULL
)
ORDER BY u.id;

A. Ninguna fila.

B. 4.

C. 1, 2.

D. 3, 4.

Ver solución del ejercicio 3

Correcta: D. La salida es 3, 4. La derecha contiene ahora 1, 1 y 2, todos no nulos. Los ids exteriores son claves primarias y tampoco son nulos. Tanto 3 como 4 son distintos de todos los valores de la subconsulta, por lo que pasan el filtro.

A mantiene el resultado anterior sin aplicar el nuevo IS NOT NULL. B confunde «sin compras pagadas» con «sin ninguna compra»: Ciro tiene una compra anulada. C invierte la pertenencia. La conclusión depende de las condiciones de estas columnas; filtrar el nulo de la derecha no sería una solución universal si también se quisiera conservar un valor nulo a la izquierda.

Ejercicio 4. NOT EXISTS correlacionado

¿Qué ids de usuarios quedan al buscar la ausencia de compras pagadas mediante una subconsulta correlacionada?

SELECT u.id
FROM usuarios AS u
WHERE NOT EXISTS (
  SELECT 1
  FROM compras AS c
  WHERE c.usuario_id = u.id
    AND c.estado = 'pagada'
)
ORDER BY u.id;

A. 1, 2, 3, 4.

B. 3, 4.

C. 3.

D. 1, 2.

Ver solución del ejercicio 4

Correcta: B. Devuelve 3, 4. Para cada usuario se pregunta si existe una compra pagada con su id. Ada y Berta tienen al menos una; Ciro y Duna no. El NOT niega esa existencia. La compra 104, con usuario_id NULL, no satisface la igualdad con ningún id exterior.

A olvida las coincidencias de 1 y 2. C excluye sin motivo a Duna. D devuelve a quienes sí tienen una compra pagada. Aquí y en el caso 3 coinciden las filas devueltas, con los datos y las restricciones de este ejercicio; no hemos probado una equivalencia sin condiciones para cualquier consulta.

Ejercicio 5. Olvidar la correlación cambia la pregunta

Aquí la subconsulta no referencia a u. ¿Qué ids de usuarios devuelve la consulta?

SELECT u.id
FROM usuarios AS u
WHERE EXISTS (
  SELECT 1
  FROM compras AS c
  WHERE c.estado = 'pagada'
)
ORDER BY u.id;

A. 1, 2.

B. 1, 2, 3, 4.

C. 3, 4.

D. Ninguna fila.

Ver solución del ejercicio 5

Correcta: B. Devuelve 1, 2, 3, 4. La subconsulta solo pregunta si hay alguna compra pagada en la tabla. Las hay, y ese hecho no depende del usuario exterior. Por tanto, EXISTS es verdadero para cada una de las cuatro filas de usuarios.

A interpreta una relación por usuario que el código no contiene. C niega además la condición. D confunde «sin correlación» con un error de sintaxis o con ausencia de resultados. La consulta es válida: lo que cambia es su significado. No se está midiendo cuántas veces la ejecuta internamente el optimizador.

Ejercicio 6. Ninguna compra frente a ninguna pagada

Ahora se busca no tener ninguna compra, sea cual sea su estado. ¿Qué id devuelve la consulta?

SELECT u.id
FROM usuarios AS u
WHERE NOT EXISTS (
  SELECT 1
  FROM compras AS c
  WHERE c.usuario_id = u.id
)
ORDER BY u.id;

A. 3, 4.

B. 1, 2, 3.

C. Ninguna fila.

D. 4.

Ver solución del ejercicio 6

Correcta: D. Solo devuelve 4. Se ha quitado el filtro de estado: la compra anulada 105 sí demuestra que existe una fila de compras relacionada con Ciro. Duna no tiene ninguna coincidencia.

A mantiene la salida de «sin compras pagadas» aunque la pregunta ha cambiado. B devuelve los usuarios que sí tienen alguna compra. C trata la fila con usuario_id NULL como si estuviera relacionada con todo el mundo. La igualdad NULL = u.id no resulta verdadera para ninguno de estos ids. No interpretar un estado como pagado no equivale a eliminar la fila de la tabla.

Ejercicio 7. La derecha vacía es un caso distinto

La subconsulta no devuelve filas porque ningún usuario tiene id negativo. ¿Qué ids de compras devuelve esta consulta?

SELECT c.id
FROM compras AS c
WHERE c.usuario_id NOT IN (
  SELECT u.id
  FROM usuarios AS u
  WHERE u.id < 0
)
ORDER BY c.id;

A. 101, 102, 103, 104, 105.

B. 101, 102, 103, 105.

C. Ninguna fila.

D. 104.

Ver solución del ejercicio 7

Correcta: A. Devuelve 101, 102, 103, 104, 105. En SQLite, NOT IN frente a una subconsulta vacía es verdadero incluso cuando el valor de la izquierda es NULL. Por eso pasa también la compra 104.

B aplica al conjunto vacío una conclusión sobre nulos que corresponde a otro caso. C confunde «ninguna fila en la subconsulta» con «ninguna fila exterior». D conserva únicamente el nulo sin fundamento. Se utiliza una subconsulta válida que produce cero filas; no se está escribiendo una lista literal vacía IN (), cuya admisión sintáctica difiere entre gestores.

id de comprausuario_idNOT IN frente a la subconsulta vacía
1011Verdadero
1021Verdadero
1032Verdadero
104NULLVerdadero
1053Verdadero

Ejercicio 8. EXISTS con una columna que contiene NULL

La subconsulta proyecta NULL en lugar de 1 y conserva la correlación por usuario. ¿Qué ids devuelve la consulta?

SELECT u.id
FROM usuarios AS u
WHERE EXISTS (
  SELECT NULL
  FROM compras AS c
  WHERE c.usuario_id = u.id
)
ORDER BY u.id;

A. Ninguna fila.

B. 1, 2, 3, 4.

C. 1, 2, 3.

D. 4.

Ver solución del ejercicio 8

Correcta: C. Devuelve 1, 2, 3. En esta subconsulta sencilla, EXISTS comprueba si hay filas, no si el valor seleccionado es no nulo. Hay filas relacionadas con Ada, Berta y Ciro; cada una proyecta NULL. Para Duna no hay ninguna.

A interpreta NULL como si destruyera la fila proyectada. B incluye un usuario sin coincidencias. D invierte la condición. El SELECT NULL está dentro de EXISTS: no se ha sustituido la subconsulta por un único valor escalar nulo. Esta distinción permite explicar el resultado sin convertir NULL en cero ni en una cadena de texto.

Clave y forma de corregirte

La clave es CADBBDAC. Antes de memorizarla, escribe el resultado de la subconsulta y marca el predicado exterior como verdadero, falso o desconocido. Solo después aplica WHERE y ordena los ids.

Si fallas los casos 1 a 3, revisa la presencia del nulo y el resultado de la negación. Si fallas el 4 o el 5, busca qué referencia al usuario exterior aparece realmente en el código. El 6 modifica el requisito de estado; el 7 cambia el número de filas de la subconsulta; el 8 conserva filas aunque proyecten un valor nulo.

Las ocho salidas y los diagnósticos de predicados se han comprobado sobre bases independientes con estos mismos datos. La verificación estudia resultados: no demuestra que EXISTS sea siempre más rápido que IN ni recomienda reescribir consultas sin comprobar su significado.

Continuar el estudio de SQL

Los ocho ejercicios de UNION, INTERSECT y EXCEPT trabajan operaciones entre resultados completos, con sus propias reglas de duplicados y nulos. Permiten separar una operación de conjuntos de una condición de pertenencia.

El material de Ingeniería Superior Informàtica del IMAS Mallorca incluye subconsultas y predicados EXISTS e IN en «SQL: el sublenguaje DML y la consulta de datos». Es un producto de pago cuya ficha detalla el alcance. Esta práctica desarrolla resultados concretos; no atribuye SQLite ni este formato a una convocatoria.

Preguntas frecuentes

¿Un NULL a la derecha de IN impide cualquier coincidencia?

No. En el caso 1, los ids 1 y 2 encuentran una igualdad y pasan el filtro. Para 3 y 4 no hay igualdad y la presencia del nulo deja el resultado desconocido.

¿Por qué NOT IN no devuelve 3 y 4 en el caso 2?

Porque la subconsulta contiene NULL. Sin coincidencia verdadera, la negación no produce verdadero para esos ids. WHERE descarta tanto falso como desconocido.

¿NOT EXISTS y NOT IN son intercambiables sin comprobar nada?

No se debe asumir. Los casos 3 y 4 coinciden porque sus datos y condiciones están fijados. El caso 2 muestra cómo cambia NOT IN cuando la derecha contiene NULL.

¿EXISTS con SELECT NULL puede ser verdadero?

Sí. En el caso 8 hay filas correlacionadas para 1, 2 y 3, aunque su columna proyectada sea NULL. EXISTS comprueba la existencia de filas de esa subconsulta.

¿Se han ejecutado estas consultas en todos los gestores?

No. Las ocho consultas y sus diagnósticos se han ejecutado en SQLite 3.53.4. La documentación de PostgreSQL se usa como referencia conceptual, sin afirmar ejecución allí ni compatibilidad universal.