SQL

Tutorial SQL: Creación y uso de subqueries

Aprende a crear y usar subconsultas en SQL para consultas complejas con ejemplos prácticos y técnicas avanzadas en WHERE, FROM y correlacionadas.

Aprende SQL y certifícate

Subconsultas en WHERE

Las subconsultas (también llamadas subqueries o consultas anidadas) son una técnica fundamental en SQL que permite utilizar el resultado de una consulta dentro de otra. Cuando colocamos una subconsulta en la cláusula WHERE, estamos usando el resultado de esa consulta interna como criterio de filtrado para la consulta principal.

Estructura básica

Una subconsulta en WHERE sigue este patrón general:

SELECT columnas
FROM tabla
WHERE columna operador (SELECT columna FROM otra_tabla WHERE condición);

El operador puede ser de comparación (=, >, <, >=, <=, <>), de pertenencia (IN, NOT IN) o de cuantificación (ANY, ALL, SOME).

Tipos de subconsultas en WHERE

Dependiendo del resultado que devuelve la subconsulta, podemos clasificarlas en:

  • Subconsultas escalares: Devuelven un único valor (una fila y una columna).
  • Subconsultas de fila única: Devuelven múltiples columnas pero una sola fila.
  • Subconsultas de múltiples filas: Devuelven una columna con múltiples filas.
  • Subconsultas de múltiples columnas y filas: Devuelven varias columnas y filas.

Subconsultas escalares

Son las más simples y se utilizan con operadores de comparación. Veamos un ejemplo:

-- Encontrar empleados con salario mayor que el promedio
SELECT nombre, salario
FROM empleados
WHERE salario > (SELECT AVG(salario) FROM empleados);

En este ejemplo, la subconsulta (SELECT AVG(salario) FROM empleados) calcula el salario promedio y devuelve un único valor numérico. La consulta principal selecciona solo aquellos empleados cuyo salario supera ese promedio.

Subconsultas con IN y NOT IN

Cuando una subconsulta devuelve múltiples filas, podemos usar los operadores IN o NOT IN:

-- Encontrar productos que han sido pedidos
SELECT nombre, precio
FROM productos
WHERE id IN (SELECT producto_id FROM pedidos);

-- Encontrar productos que nunca han sido pedidos
SELECT nombre, precio
FROM productos
WHERE id NOT IN (SELECT producto_id FROM pedidos WHERE producto_id IS NOT NULL);

Nota: Es importante incluir la condición IS NOT NULL cuando usamos NOT IN con subconsultas, ya que si la subconsulta devuelve algún valor NULL, el resultado completo será vacío.

Subconsultas con EXISTS y NOT EXISTS

Los operadores EXISTS y NOT EXISTS verifican si la subconsulta devuelve algún resultado:

-- Encontrar clientes que han realizado al menos un pedido
SELECT nombre, email
FROM clientes c
WHERE EXISTS (SELECT 1 FROM pedidos p WHERE p.cliente_id = c.id);

-- Encontrar clientes que nunca han realizado pedidos
SELECT nombre, email
FROM clientes c
WHERE NOT EXISTS (SELECT 1 FROM pedidos p WHERE p.cliente_id = c.id);

La subconsulta con EXISTS suele usar SELECT 1 porque solo importa si devuelve alguna fila, no qué valores contiene.

Subconsultas con ANY, SOME y ALL

Estos operadores permiten comparaciones más complejas:

  • ANY/SOME: La condición es verdadera si se cumple para al menos uno de los valores devueltos por la subconsulta.
  • ALL: La condición es verdadera si se cumple para todos los valores devueltos por la subconsulta.
-- Encontrar productos más caros que al menos un producto de la categoría 'Básica'
SELECT nombre, precio
FROM productos
WHERE precio > ANY (SELECT precio FROM productos WHERE categoria = 'Básica');

-- Encontrar productos más caros que todos los productos de la categoría 'Básica'
SELECT nombre, precio
FROM productos
WHERE precio > ALL (SELECT precio FROM productos WHERE categoria = 'Básica');

Subconsultas en condiciones compuestas

Podemos combinar subconsultas con operadores lógicos (AND, OR) para crear condiciones más complejas:

-- Encontrar empleados que ganan más que el promedio y trabajan en el departamento de ventas
SELECT nombre, salario
FROM empleados
WHERE salario > (SELECT AVG(salario) FROM empleados)
AND departamento_id = (SELECT id FROM departamentos WHERE nombre = 'Ventas');

Subconsultas anidadas

Es posible anidar múltiples niveles de subconsultas:

-- Encontrar clientes que han comprado el producto más caro
SELECT nombre
FROM clientes
WHERE id IN (
    SELECT cliente_id
    FROM pedidos
    WHERE producto_id = (
        SELECT id
        FROM productos
        WHERE precio = (SELECT MAX(precio) FROM productos)
    )
);

Consideraciones de rendimiento

Las subconsultas en WHERE pueden afectar el rendimiento, especialmente cuando:

  • Se anidan múltiples niveles de subconsultas
  • Se utilizan subconsultas correlacionadas (que hacen referencia a la consulta externa)
  • La subconsulta se ejecuta una vez por cada fila evaluada en la consulta principal

En estos casos, considerar alternativas como:

-- En lugar de NOT IN (que puede ser lento):
SELECT p.nombre, p.precio
FROM productos p
LEFT JOIN pedidos pd ON p.id = pd.producto_id
WHERE pd.id IS NULL;

-- En lugar de subconsultas anidadas:
SELECT c.nombre
FROM clientes c
JOIN pedidos pd ON c.id = pd.cliente_id
JOIN productos p ON pd.producto_id = p.id
WHERE p.precio = (SELECT MAX(precio) FROM productos);

Ejemplos prácticos

Veamos algunos ejemplos más complejos:

Ejemplo 1: Encontrar departamentos que tienen empleados con salario superior a 50000:

SELECT nombre
FROM departamentos
WHERE id IN (SELECT departamento_id FROM empleados WHERE salario > 50000);

Ejemplo 2: Encontrar productos que no han sido vendidos en el último mes:

SELECT nombre
FROM productos
WHERE id NOT IN (
    SELECT DISTINCT producto_id
    FROM pedidos
    WHERE fecha_pedido >= CURRENT_DATE - INTERVAL '1 month'
);

Ejemplo 3: Encontrar clientes que han gastado más que el promedio:

SELECT c.nombre, SUM(p.precio) as total_gastado
FROM clientes c
JOIN pedidos pd ON c.id = pd.cliente_id
JOIN productos p ON pd.producto_id = p.id
GROUP BY c.id, c.nombre
HAVING SUM(p.precio) > (
    SELECT AVG(total)
    FROM (
        SELECT SUM(p2.precio) as total
        FROM pedidos pd2
        JOIN productos p2 ON pd2.producto_id = p2.id
        GROUP BY pd2.cliente_id
    ) as gastos_cliente
);

Las subconsultas en WHERE son una herramienta versátil para crear consultas complejas y precisas, permitiéndote filtrar resultados basados en datos de otras tablas o en cálculos derivados de los propios datos.

Subconsultas en FROM

Las subconsultas en la cláusula FROM permiten utilizar el resultado de una consulta como si fuera una tabla temporal dentro de nuestra consulta principal. Esta técnica es especialmente útil cuando necesitamos realizar operaciones sobre un conjunto de resultados intermedios antes de obtener el resultado final.

Estructura básica

La sintaxis general de una subconsulta en FROM es:

SELECT columnas
FROM (SELECT columnas FROM tabla WHERE condición) AS alias
WHERE condición;

El alias es obligatorio en la mayoría de los sistemas de gestión de bases de datos, ya que toda tabla o conjunto de resultados en la cláusula FROM debe tener un nombre.

Ventajas de las subconsultas en FROM

Las subconsultas en FROM ofrecen varias ventajas:

  • Permiten trabajar con resultados intermedios que no existen como tablas físicas
  • Facilitan la simplificación de consultas complejas dividiéndolas en partes más manejables
  • Posibilitan realizar cálculos o transformaciones sobre un conjunto de datos antes de utilizarlo

Casos de uso comunes

1. Filtrar datos pre-agregados

Uno de los usos más frecuentes es cuando necesitamos filtrar datos que han sido previamente agregados:

-- Encontrar departamentos con salario promedio superior a 50000
SELECT dept_nombre, salario_promedio
FROM (
    SELECT d.nombre AS dept_nombre, AVG(e.salario) AS salario_promedio
    FROM departamentos d
    JOIN empleados e ON d.id = e.departamento_id
    GROUP BY d.nombre
) AS dept_stats
WHERE salario_promedio > 50000
ORDER BY salario_promedio DESC;

En este ejemplo, primero calculamos el salario promedio por departamento en la subconsulta, y luego filtramos solo aquellos departamentos cuyo promedio supera los 50000.

2. Trabajar con resultados de funciones de ventana

Las subconsultas en FROM son ideales cuando trabajamos con funciones de ventana (window functions):

-- Encontrar empleados que ganan más que el promedio de su departamento
SELECT nombre, departamento, salario, diferencia_porcentual
FROM (
    SELECT 
        e.nombre,
        d.nombre AS departamento,
        e.salario,
        ROUND((e.salario - AVG(e.salario) OVER (PARTITION BY d.id)) / AVG(e.salario) OVER (PARTITION BY d.id) * 100, 2) AS diferencia_porcentual
    FROM empleados e
    JOIN departamentos d ON e.departamento_id = d.id
) AS comparativa
WHERE diferencia_porcentual > 0
ORDER BY diferencia_porcentual DESC;

En este caso, la subconsulta calcula la diferencia porcentual entre el salario de cada empleado y el promedio de su departamento, y luego filtramos solo aquellos con diferencia positiva.

3. Crear tablas derivadas para simplificar JOINs complejos

Las subconsultas en FROM pueden simplificar consultas con múltiples JOINs:

-- Obtener estadísticas de ventas por categoría y región
SELECT c.nombre AS categoria, r.region_nombre, v.total_ventas, v.promedio_venta
FROM categorias c
JOIN (
    SELECT 
        p.categoria_id,
        v.region_id,
        SUM(v.cantidad * p.precio) AS total_ventas,
        AVG(v.cantidad * p.precio) AS promedio_venta
    FROM ventas v
    JOIN productos p ON v.producto_id = p.id
    GROUP BY p.categoria_id, v.region_id
) AS v ON c.id = v.categoria_id
JOIN (
    SELECT id, nombre AS region_nombre
    FROM regiones
) AS r ON v.region_id = r.id
ORDER BY c.nombre, total_ventas DESC;

Aquí hemos creado dos tablas derivadas: una para las estadísticas de ventas agrupadas por categoría y región, y otra para simplificar la información de regiones.

Subconsultas anidadas en FROM

Es posible anidar múltiples niveles de subconsultas en la cláusula FROM:

-- Encontrar la diferencia entre las ventas mensuales y el promedio histórico
SELECT 
    año, 
    mes, 
    ventas_mes,
    promedio_historico,
    ventas_mes - promedio_historico AS diferencia
FROM (
    SELECT 
        vm.año,
        vm.mes,
        vm.ventas_mes,
        (SELECT AVG(ventas_mes) 
         FROM (
             SELECT EXTRACT(YEAR FROM fecha) AS año,
                    EXTRACT(MONTH FROM fecha) AS mes,
                    SUM(monto) AS ventas_mes
             FROM ventas
             GROUP BY año, mes
         ) AS hist
         WHERE hist.año < vm.año OR (hist.año = vm.año AND hist.mes < vm.mes)
        ) AS promedio_historico
    FROM (
        SELECT 
            EXTRACT(YEAR FROM fecha) AS año,
            EXTRACT(MONTH FROM fecha) AS mes,
            SUM(monto) AS ventas_mes
        FROM ventas
        GROUP BY año, mes
    ) AS vm
) AS comparativa
ORDER BY año, mes;

Este ejemplo muestra cómo podemos anidar subconsultas para calcular estadísticas complejas, como la diferencia entre las ventas mensuales y el promedio histórico hasta ese momento.

Combinación con Common Table Expressions (CTEs)

Aunque las CTEs (WITH) se verán en lecciones avanzadas, es importante mencionar que son una alternativa a las subconsultas en FROM que mejora la legibilidad. Aquí un ejemplo equivalente usando subconsultas en FROM:

-- Encontrar clientes que han comprado todos los productos de una categoría
SELECT cliente_nombre, categoria_nombre
FROM (
    SELECT 
        c.nombre AS cliente_nombre,
        cat.nombre AS categoria_nombre,
        COUNT(DISTINCT p.id) AS productos_comprados,
        (
            SELECT COUNT(*)
            FROM productos
            WHERE categoria_id = cat.id
        ) AS total_productos_categoria
    FROM clientes c
    JOIN pedidos pe ON c.id = pe.cliente_id
    JOIN productos p ON pe.producto_id = p.id
    JOIN categorias cat ON p.categoria_id = cat.id
    GROUP BY c.id, c.nombre, cat.id, cat.nombre
) AS estadisticas
WHERE productos_comprados = total_productos_categoria;

Consideraciones de rendimiento

Al utilizar subconsultas en FROM, ten en cuenta:

  • Cada subconsulta se ejecuta primero y su resultado se materializa temporalmente
  • Las subconsultas complejas o que devuelven muchos datos pueden afectar el rendimiento
  • El optimizador de consultas puede no siempre elegir el plan de ejecución óptimo

Para mejorar el rendimiento:

  • Incluye solo las columnas necesarias en la subconsulta
  • Aplica filtros dentro de la subconsulta cuando sea posible
  • Considera usar índices en las columnas utilizadas para filtrar o unir
-- Versión optimizada con filtros en la subconsulta
SELECT cliente, total_gastado
FROM (
    SELECT 
        c.nombre AS cliente,
        SUM(p.precio * pe.cantidad) AS total_gastado
    FROM clientes c
    JOIN pedidos pe ON c.id = pe.cliente_id
    JOIN productos p ON pe.producto_id = p.id
    WHERE pe.fecha >= CURRENT_DATE - INTERVAL '1 year'
    AND c.estado = 'activo'
    GROUP BY c.id, c.nombre
) AS gastos_cliente
WHERE total_gastado > 1000
ORDER BY total_gastado DESC;

Ejemplos prácticos adicionales

Ejemplo 1: Calcular el ranking de ventas por producto:

SELECT 
    producto,
    categoria,
    ventas_totales,
    ranking_global,
    ranking_categoria
FROM (
    SELECT 
        p.nombre AS producto,
        c.nombre AS categoria,
        SUM(v.cantidad * p.precio) AS ventas_totales,
        RANK() OVER (ORDER BY SUM(v.cantidad * p.precio) DESC) AS ranking_global,
        RANK() OVER (PARTITION BY c.id ORDER BY SUM(v.cantidad * p.precio) DESC) AS ranking_categoria
    FROM ventas v
    JOIN productos p ON v.producto_id = p.id
    JOIN categorias c ON p.categoria_id = c.id
    GROUP BY p.id, p.nombre, c.id, c.nombre
) AS rankings
WHERE ranking_global <= 10 OR ranking_categoria <= 3
ORDER BY categoria, ranking_categoria;

Ejemplo 2: Analizar tendencias de crecimiento:

SELECT 
    año,
    mes,
    ventas,
    ventas_mes_anterior,
    ROUND((ventas - ventas_mes_anterior) / ventas_mes_anterior * 100, 2) AS crecimiento_porcentual
FROM (
    SELECT 
        EXTRACT(YEAR FROM fecha) AS año,
        EXTRACT(MONTH FROM fecha) AS mes,
        SUM(monto) AS ventas,
        LAG(SUM(monto), 1) OVER (ORDER BY EXTRACT(YEAR FROM fecha), EXTRACT(MONTH FROM fecha)) AS ventas_mes_anterior
    FROM ventas
    GROUP BY año, mes
) AS tendencia
WHERE ventas_mes_anterior IS NOT NULL
ORDER BY año, mes;

Ejemplo 3: Identificar anomalías en transacciones:

SELECT 
    cliente_id,
    cliente_nombre,
    fecha,
    monto,
    promedio_cliente,
    ROUND((monto - promedio_cliente) / promedio_cliente * 100, 2) AS desviacion_porcentual
FROM (
    SELECT 
        c.id AS cliente_id,
        c.nombre AS cliente_nombre,
        t.fecha,
        t.monto,
        (SELECT AVG(monto) FROM transacciones WHERE cliente_id = c.id) AS promedio_cliente
    FROM clientes c
    JOIN transacciones t ON c.id = t.cliente_id
) AS analisis
WHERE ABS(monto - promedio_cliente) > promedio_cliente * 2
ORDER BY desviacion_porcentual DESC;

Las subconsultas en FROM son una herramienta fundamental para trabajar con conjuntos de datos intermedios y realizar análisis complejos en SQL. Dominando esta técnica, podrás construir consultas más modulares y resolver problemas de análisis de datos que serían difíciles de abordar con consultas simples.

Subconsultas correlacionadas

Las subconsultas correlacionadas representan una técnica avanzada en SQL donde la consulta interna hace referencia a una o más columnas de la consulta externa. A diferencia de las subconsultas independientes, que se ejecutan una sola vez, las correlacionadas se ejecutan repetidamente, una vez por cada fila procesada en la consulta principal.

Estructura y funcionamiento

Una subconsulta correlacionada sigue este patrón general:

SELECT columnas
FROM tabla_externa e
WHERE columna_externa operador (
    SELECT expresión
    FROM tabla_interna i
    WHERE i.columna = e.columna
);

La característica distintiva es la referencia a la tabla externa (e.columna) dentro de la subconsulta. Esta referencia crea una "correlación" entre ambas consultas, haciendo que la subconsulta interna dependa de cada fila que se está procesando en la consulta externa.

Proceso de ejecución

Para entender mejor cómo funcionan, veamos el proceso de ejecución:

  1. El motor SQL selecciona una fila de la tabla externa
  2. Ejecuta la subconsulta interna utilizando valores de la fila seleccionada
  3. Utiliza el resultado de la subconsulta para evaluar la condición WHERE
  4. Si la condición es verdadera, incluye la fila en el resultado final
  5. Repite el proceso para cada fila de la tabla externa

Este comportamiento es fundamentalmente diferente de las subconsultas normales, que se ejecutan una sola vez y producen un conjunto de resultados que luego se utiliza en la consulta principal.

Ejemplos básicos

Ejemplo 1: Encontrar empleados que ganan más que el promedio de su departamento:

SELECT e.nombre, e.salario, e.departamento_id
FROM empleados e
WHERE e.salario > (
    SELECT AVG(salario)
    FROM empleados e2
    WHERE e2.departamento_id = e.departamento_id
);

En este ejemplo, para cada empleado en la consulta externa, la subconsulta calcula el salario promedio de su departamento específico y luego compara el salario del empleado con ese promedio.

Ejemplo 2: Encontrar clientes que han realizado al menos un pedido superior a 1000:

SELECT c.id, c.nombre
FROM clientes c
WHERE EXISTS (
    SELECT 1
    FROM pedidos p
    WHERE p.cliente_id = c.id
    AND p.total > 1000
);

Aquí, la subconsulta verifica si existe al menos un pedido con total mayor a 1000 para el cliente actual que se está evaluando en la consulta externa.

Operadores comunes con subconsultas correlacionadas

EXISTS y NOT EXISTS

Los operadores EXISTS y NOT EXISTS son particularmente útiles con subconsultas correlacionadas:

-- Encontrar productos que nunca han sido pedidos
SELECT p.nombre, p.precio
FROM productos p
WHERE NOT EXISTS (
    SELECT 1
    FROM pedidos_detalle pd
    WHERE pd.producto_id = p.id
);

-- Encontrar departamentos donde todos los empleados ganan más de 30000
SELECT d.nombre
FROM departamentos d
WHERE NOT EXISTS (
    SELECT 1
    FROM empleados e
    WHERE e.departamento_id = d.id
    AND e.salario <= 30000
);

El segundo ejemplo muestra una técnica común: usar NOT EXISTS con una condición negada para implementar lógica de "todos" (equivalente a la cuantificación universal en lógica).

Comparaciones con ANY, ALL y SOME

Estos operadores también funcionan bien con subconsultas correlacionadas:

-- Encontrar empleados que ganan más que cualquier empleado del departamento de Marketing
SELECT e.nombre, e.salario
FROM empleados e
WHERE e.salario > ANY (
    SELECT e2.salario
    FROM empleados e2
    JOIN departamentos d ON e2.departamento_id = d.id
    WHERE d.nombre = 'Marketing'
    AND e2.departamento_id != e.departamento_id
);

Casos de uso avanzados

Actualización con subconsultas correlacionadas

Las subconsultas correlacionadas son útiles en operaciones de actualización:

-- Actualizar el precio de productos basado en el promedio de su categoría
UPDATE productos p
SET precio = precio * 1.1
WHERE precio < (
    SELECT AVG(p2.precio) * 0.8
    FROM productos p2
    WHERE p2.categoria_id = p.categoria_id
);

Este ejemplo aumenta en un 10% el precio de los productos que están por debajo del 80% del precio promedio de su categoría.

Eliminación con subconsultas correlacionadas

También podemos usar subconsultas correlacionadas para operaciones DELETE:

-- Eliminar clientes sin pedidos en los últimos 2 años
DELETE FROM clientes c
WHERE NOT EXISTS (
    SELECT 1
    FROM pedidos p
    WHERE p.cliente_id = c.id
    AND p.fecha_pedido >= CURRENT_DATE - INTERVAL '2 year'
);

Subconsultas correlacionadas en SELECT

Aunque es menos común, también podemos usar subconsultas correlacionadas en la lista SELECT:

-- Mostrar empleados con su salario y el promedio de su departamento
SELECT 
    e.nombre,
    e.salario,
    (
        SELECT AVG(e2.salario)
        FROM empleados e2
        WHERE e2.departamento_id = e.departamento_id
    ) AS promedio_departamento,
    e.salario - (
        SELECT AVG(e2.salario)
        FROM empleados e2
        WHERE e2.departamento_id = e.departamento_id
    ) AS diferencia
FROM empleados e;

Subconsultas correlacionadas vs. JOINs

Muchas consultas que usan subconsultas correlacionadas pueden reescribirse usando JOINs, a menudo con mejor rendimiento:

-- Versión con subconsulta correlacionada
SELECT e.nombre, e.salario
FROM empleados e
WHERE e.salario > (
    SELECT AVG(salario)
    FROM empleados
    WHERE departamento_id = e.departamento_id
);

-- Versión equivalente con JOIN
SELECT e.nombre, e.salario
FROM empleados e
JOIN (
    SELECT departamento_id, AVG(salario) AS salario_promedio
    FROM empleados
    GROUP BY departamento_id
) AS promedios ON e.departamento_id = promedios.departamento_id
WHERE e.salario > promedios.salario_promedio;

La versión con JOIN suele ser más eficiente porque la subconsulta se ejecuta una sola vez, mientras que la correlacionada se ejecuta para cada fila de la tabla externa.

Consideraciones de rendimiento

Las subconsultas correlacionadas pueden afectar significativamente el rendimiento porque:

  • Se ejecutan una vez por cada fila de la consulta externa
  • Pueden causar planes de ejecución ineficientes
  • Requieren más recursos de procesamiento

Para optimizar el rendimiento:

  • Considera reescribir usando JOINs cuando sea posible
  • Asegúrate de tener índices en las columnas de correlación
  • Limita el número de filas en la consulta externa con condiciones adicionales
-- Versión optimizada con filtro adicional en la consulta externa
SELECT e.nombre, e.salario
FROM empleados e
WHERE e.departamento_id IN (1, 2, 3)  -- Filtro adicional
AND e.salario > (
    SELECT AVG(salario)
    FROM empleados
    WHERE departamento_id = e.departamento_id
);

Ejemplos prácticos adicionales

Ejemplo 1: Encontrar productos que tienen un precio superior al promedio de su categoría:

SELECT p.nombre, p.precio, c.nombre AS categoria
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
WHERE p.precio > (
    SELECT AVG(p2.precio)
    FROM productos p2
    WHERE p2.categoria_id = p.categoria_id
)
ORDER BY c.nombre, p.precio DESC;

Ejemplo 2: Identificar empleados que tienen más antigüedad que todos los empleados de su departamento actual:

SELECT e.nombre, e.fecha_contratacion, d.nombre AS departamento
FROM empleados e
JOIN departamentos d ON e.departamento_id = d.id
WHERE NOT EXISTS (
    SELECT 1
    FROM empleados e2
    WHERE e2.departamento_id = e.departamento_id
    AND e2.id != e.id
    AND e2.fecha_contratacion < e.fecha_contratacion
);

Este ejemplo encuentra los empleados más antiguos de cada departamento utilizando NOT EXISTS con una subconsulta correlacionada.

Ejemplo 3: Encontrar clientes que han comprado todos los productos de una categoría específica:

SELECT c.nombre
FROM clientes c
WHERE NOT EXISTS (
    SELECT p.id
    FROM productos p
    WHERE p.categoria_id = 5  -- Categoría específica
    AND NOT EXISTS (
        SELECT 1
        FROM pedidos pe
        JOIN pedidos_detalle pd ON pe.id = pd.pedido_id
        WHERE pe.cliente_id = c.id
        AND pd.producto_id = p.id
    )
);

Este ejemplo utiliza subconsultas correlacionadas anidadas para implementar la lógica de "para todo" (cuantificación universal): "clientes para los que no existe ningún producto de la categoría 5 que no hayan comprado".

Cuándo usar subconsultas correlacionadas

Las subconsultas correlacionadas son especialmente útiles cuando:

  • Necesitas comparar valores de una fila con agregaciones de grupos relacionados
  • Quieres implementar lógica de cuantificación (todos, alguno, ninguno)
  • La lógica de la consulta es más clara y legible con subconsultas que con JOINs
  • Necesitas realizar comprobaciones de existencia condicionales

A pesar de sus posibles implicaciones de rendimiento, las subconsultas correlacionadas son una herramienta valiosa en el arsenal de cualquier desarrollador SQL, permitiendo expresar lógica compleja de manera concisa y clara.

Aprende SQL online

Otros ejercicios de programación de SQL

Evalúa tus conocimientos de esta lección Creación y uso de subqueries con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Tipos de datos

Test

Inserción de datos: INSERT INTO

Test

Filtrado de grupos de resultados con HAVING

Test

Uso de índices y particiones

Test

Renombrar tablas y bases de datos: RENAME

Test

Uso de vistas

Test

Uso de INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN

Test

Agrupación de resultados con GROUP BY

Test

Creación y uso de subqueries

Test

Sentencias INSERT

Código

Copias de seguridad y restauración de bases de datos

Test

Uso de INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN

Código

Instalación de MySQL

Test

Relaciones entre tablas

Código

Eliminación de datos: DELETE

Test

Creación de bases de datos y tablas: CREATE DATABASE, CREATE TABLE

Test

Creación y uso de funciones

Test

Creación de tablas e inserción de datos con SQL

Proyecto

Uso de funciones agregadas: COUNT, SUM, AVG, MAX, MIN

Test

Optimización de consultas

Test

Introducción a SQL

Test

Triggers y eventos

Test

Clasificación de resultados con ORDER BY

Test

Alterar la estructura de tablas existentes: ALTER TABLE

Test

Eliminación de datos: DELETE

Código

Instalación de PostgreSQL

Test

Creación y uso de procedimientos almacenados

Test

Consultas básicas de selección: SELECT y WHERE

Test

Vaciar tablas y bases de datos: DROP

Test

Actualización de datos: UPDATE

Test

Creación y manejo de usuarios y roles

Test

Consultas básicas de selección SELECT y WHERE

Código

Creación de bases de datos y tablas

Código

Bases de datos y tablas

Test

Actualización de datos: UPDATE

Código

Relaciones entre tablas

Test

Filtrado de valores únicos con DISTINCT

Test

Asignación y gestión de permisos

Test

Todas las lecciones de SQL

Accede a todas las lecciones de SQL y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Sql

Introducción Y Entorno

Ddl Y Dml

Introducción Y Entorno

Instalación De Mysql

Introducción Y Entorno

Instalación De Postgresql

Introducción Y Entorno

Tipos De Datos

Introducción Y Entorno

Bases De Datos Y Tablas

Introducción Y Entorno

Sistemas De Gestión De Bases De Datos

Introducción Y Entorno

Tipos De Bases De Datos

Introducción Y Entorno

Creación De Bases De Datos Y Tablas: Create Database, Create Table

Sintaxis Dml Crud

Consultas Básicas De Selección: Select Y Where

Sintaxis Dml Crud

Inserción De Datos: Insert Into

Sintaxis Dml Crud

Actualización De Datos: Update

Sintaxis Dml Crud

Eliminación De Datos: Delete

Sintaxis Dml Crud

Introducción A Dml

Sintaxis Dml Crud

Consultar Datos: Select

Sintaxis Dml Crud

Clasificación De Resultados Con Order By

Filtros Y Clasificación

Filtrado De Valores Únicos Con Distinct

Filtros Y Clasificación

Paginación Con Limit Y Offset

Filtros Y Clasificación

Alterar La Estructura De Tablas Existentes: Alter Table

Sintaxis Ddl

Renombrar Tablas Y Bases De Datos: Rename

Sintaxis Ddl

Vaciar Tablas Y Bases De Datos: Drop

Sintaxis Ddl

Uso De Funciones Agregadas: Count, Sum, Avg, Max, Min

Funciones Y Agrupación

Agrupación De Resultados Con Group By

Funciones Y Agrupación

Filtrado De Grupos De Resultados Con Having

Funciones Y Agrupación

Funciones Numéricas Y Matemáticas

Funciones Y Agrupación

Funciones De Fecha Y Hora

Funciones Y Agrupación

Funciones De Texto

Funciones Y Agrupación

Many To One

Asociaciones Entre Tablas

One To Many

Asociaciones Entre Tablas

One To One

Asociaciones Entre Tablas

Many To Many

Asociaciones Entre Tablas

Relaciones Entre Tablas

Joins Y Subqueries

Uso De Inner Join, Left Join, Right Join, Full Join

Joins Y Subqueries

Creación Y Uso De Subqueries

Joins Y Subqueries

Left Join Y Right Join

Joins Y Subqueries

Full Join

Joins Y Subqueries

Cross Join Y Self Join

Joins Y Subqueries

Optimización De Consultas

Sintaxis Avanzada

Uso De Índices Y Particiones

Sintaxis Avanzada

Uso De Vistas

Sintaxis Avanzada

Triggers Y Eventos

Sintaxis Avanzada

Particiones

Sintaxis Avanzada

Restricciones E Integridad

Sintaxis Avanzada

Transacciones

Sintaxis Avanzada

Vistas Materializadas

Sintaxis Avanzada

Rollback

Sintaxis Avanzada

Vistas Con Create View

Sintaxis Avanzada

Principios Acid

Sintaxis Avanzada

Manejo De Errores Y Excepciones

Sintaxis Avanzada

Funciones Ventana

Sintaxis Avanzada

Índices

Sintaxis Avanzada

Expresiones De Tabla Comunes (Cte) Con With

Sintaxis Avanzada

Creación Y Uso De Funciones

Programación En Sql

Creación Y Uso De Procedimientos Almacenados

Programación En Sql

Variables Y Control De Flujo

Programación En Sql

Creación Y Manejo De Usuarios Y Roles

Seguridad Y Administración

Asignación Y Gestión De Permisos

Seguridad Y Administración

Copias De Seguridad Y Restauración De Bases De Datos

Seguridad Y Administración

Accede GRATIS a SQL y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender la estructura y tipos de subconsultas en la cláusula WHERE.
  • Aprender a utilizar subconsultas en la cláusula FROM para trabajar con resultados intermedios.
  • Entender el concepto y funcionamiento de las subconsultas correlacionadas.
  • Identificar casos prácticos y ejemplos de uso de subconsultas en diferentes contextos.
  • Conocer las implicaciones de rendimiento y optimizaciones al usar subconsultas en SQL.