Subconsultas SQL: tipos, ejemplos y buenas prácticas

Subconsultas SQL: tipos, ejemplos y buenas prácticas

Una subconsulta SQL es una consulta incluida dentro de otra sentencia. Su resultado puede actuar como un valor, una lista, una tabla temporal o una condición de existencia. Permite expresar preguntas como “productos por encima del promedio” o “clientes que tienen al menos un pedido”.

La clave no es anidar por anidar, sino elegir el patrón que comunique la intención y permita al optimizador trabajar bien.

Sintaxis básica

Productos con precio superior al promedio:

SELECT
    producto_id,
    nombre,
    precio
FROM productos
WHERE precio > (
    SELECT AVG(precio)
    FROM productos
);

La consulta interior devuelve un valor; la exterior lo utiliza en el filtro.

Tipos de subconsultas

Subconsulta escalar

Devuelve una fila y una columna:

SELECT
    pedido_id,
    total,
    (SELECT AVG(total) FROM pedidos) AS promedio_global
FROM pedidos;

Si devuelve más de una fila donde se espera un escalar, el motor genera un error.

Subconsulta de lista

Devuelve varios valores y se combina con IN:

SELECT nombre
FROM clientes
WHERE cliente_id IN (
    SELECT cliente_id
    FROM pedidos
    WHERE fecha >= '2026-01-01'
);

Subconsulta de tabla

Aparece en FROM y necesita alias:

SELECT
    resumen.cliente_id,
    resumen.facturacion
FROM (
    SELECT
        cliente_id,
        SUM(total) AS facturacion
    FROM pedidos
    GROUP BY cliente_id
) AS resumen
WHERE resumen.facturacion > 10000;

Subconsulta correlacionada

Hace referencia a la fila de la consulta exterior:

SELECT
    p.producto_id,
    p.categoria_id,
    p.precio
FROM productos AS p
WHERE p.precio > (
    SELECT AVG(p2.precio)
    FROM productos AS p2
    WHERE p2.categoria_id = p.categoria_id
);

Compara cada producto con el promedio de su propia categoría.

EXISTS y NOT EXISTS

EXISTS comprueba si la subconsulta devuelve al menos una fila:

SELECT
    c.cliente_id,
    c.nombre
FROM clientes AS c
WHERE EXISTS (
    SELECT 1
    FROM pedidos AS p
    WHERE p.cliente_id = c.cliente_id
      AND p.estado = 'pendiente'
);

El SELECT 1 expresa que no importa el contenido, solo la existencia. El optimizador puede detener la búsqueda cuando encuentra una coincidencia.

Clientes sin pedidos:

SELECT
    c.cliente_id,
    c.nombre
FROM clientes AS c
WHERE NOT EXISTS (
    SELECT 1
    FROM pedidos AS p
    WHERE p.cliente_id = c.cliente_id
);

El problema de NOT IN y NULL

Este patrón puede devolver cero filas inesperadamente:

WHERE cliente_id NOT IN (
    SELECT cliente_id
    FROM pedidos
)

Si la subconsulta contiene NULL, la comparación se vuelve desconocida. NOT EXISTS es más seguro para antijoins:

WHERE NOT EXISTS (
    SELECT 1
    FROM pedidos AS p
    WHERE p.cliente_id = c.cliente_id
)

Subconsulta vs JOIN

Pedidos con el nombre del cliente:

SELECT
    p.pedido_id,
    c.nombre
FROM pedidos AS p
JOIN clientes AS c
  ON c.cliente_id = p.cliente_id;

Un JOIN es natural cuando necesitas columnas de ambas tablas. EXISTS es natural cuando solo preguntas si hay relación y quieres evitar duplicar filas; la guía para recopilar datos con SQL cubre esos fundamentos.

Necesidad Patrón recomendado
Traer columnas relacionadas JOIN
Comprobar existencia EXISTS
Comprobar ausencia NOT EXISTS
Comparar con un agregado Subconsulta escalar
Organizar varias etapas CTE

El optimizador puede transformar patrones equivalentes; prioriza claridad y confirma con el plan.

Subconsulta vs CTE

Una CTE da nombre al resultado antes de la sentencia principal. Suele ser más legible cuando:

  • el cálculo se reutiliza;
  • hay varias etapas;
  • existe recursividad;
  • quieres probar bloques por separado.

Una subconsulta corta dentro de EXISTS, IN o una comparación puede ser más directa.

ANY y ALL

Mayor que al menos un valor:

WHERE precio > ANY (
    SELECT precio
    FROM productos
    WHERE categoria_id = 10
)

Mayor que todos:

WHERE precio > ALL (
    SELECT precio
    FROM productos
    WHERE categoria_id = 10
)

Revisa el comportamiento cuando la subconsulta está vacía y documenta la intención; MIN o MAX puede ser más fácil de entender en algunos casos.

Rendimiento de subconsultas

Una subconsulta correlacionada parece ejecutarse por cada fila, pero los optimizadores modernos pueden reescribirla. Aun así:

  • indexa columnas de correlación;
  • evita funciones sobre columnas filtradas si impiden usar índices;
  • devuelve solo lo necesario;
  • usa EXISTS para existencia;
  • revisa cardinalidades y el plan;
  • compara con un JOIN o preagregación en datos reales.

No asumas que una forma es siempre más rápida.

Errores comunes

  • Devolver varias filas en una comparación escalar.
  • Usar NOT IN sin considerar NULL.
  • Omitir alias en subconsultas de FROM.
  • Anidar muchos niveles y perder legibilidad.
  • Usar SELECT * dentro de una consulta derivada.
  • Duplicar filas con JOIN y corregirlas con DISTINCT sin entender la relación.
  • Ejecutar filtros tarde y procesar datos innecesarios.

Preguntas frecuentes

¿Qué es una subconsulta en SQL?
Es una consulta dentro de otra sentencia cuyo resultado se utiliza como valor, lista, tabla o condición.

¿Qué es una subconsulta correlacionada?
Es una subconsulta que usa columnas de la fila exterior y, conceptualmente, se evalúa en relación con cada fila.

¿Cuándo usar EXISTS en lugar de IN?
Cuando solo necesitas comprobar existencia, especialmente con una correlación. Para ausencia, NOT EXISTS evita la trampa de NULL de NOT IN.

¿Una subconsulta es más lenta que un JOIN?
No necesariamente. El optimizador puede convertir ambas formas en planes similares. Mide con datos y plan de ejecución.

¿Puedo usar una subconsulta en SELECT?
Sí, si devuelve un valor escalar por fila. Muchas subconsultas correlacionadas en SELECT pueden ser difíciles de mantener.

¿Cuál es la diferencia entre subconsulta y CTE?
La CTE asigna un nombre temporal antes de la consulta principal; la subconsulta se escribe directamente en el lugar donde se usa.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir